commit cc8ed39b240180b58810784f844e253263594ac3 Author: Eric Andersen Date: Tue Oct 5 16:24:54 1999 +0000 Initial revision diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 000000000..a1a4c366c --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +busybox diff --git a/Changelog b/Changelog new file mode 100644 index 000000000..e00f7281e --- /dev/null +++ b/Changelog @@ -0,0 +1,8 @@ +0.27 + Mount now supports -a, and -t auto. + Mount now updates mtab correctly for 'ro'. + More checks screen rows size, outputs bytes percentage. + Printf added as module. +0.26 + Touch now creates files. -c option for no create. + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..cf73b2c33 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Original release code (unless otherwise noted) +Copyright 1995, 1996 Bruce Perens + +mkswap +Copyright 1991 Linus Torvalds + +tiny-ls(ls) +Copyright 1996 Brian Candler + +tarcat, loadkmap, various fixes, Debian maintenance +Copyright 1998 Enrique Zanardi + +more(v2), makedevs, dutmp, modularization, auto links file, +various fixes, Linux Router Project maintenance +Copyright 1998 Dave Cinege + +mini-gzip(gzip), mini-netcat(mnc) +Copyright 1998 Charles P. Wright + + +Please see the top of the source files for more precise indivigual +copyright and license info. + +This program suite may be distributed under the GNU General Public License. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..bef25a3ae --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ + +VERSION=0.29alpha1 +BUILDTIME=$(shell date "+%Y%m%d-%H%M") + +#This will choke on a non-debian system +ARCH=`uname -m | sed -e 's/i.86/i386/' | sed -e 's/sparc.*/sparc/'` + + +STRIP= strip --remove-section=.note --remove-section=.comment busybox +LDFLAGS= -s + +# -D_GNU_SOURCE is needed because environ is used in init.c +CFLAGS=-Wall -O2 -fomit-frame-pointer -fno-builtin -D_GNU_SOURCE +# For debugging only +#CFLAGS=-Wall -g -D_GNU_SOURCE +LIBRARIES=-lc +OBJECTS=$(shell ./busybox.obj) utility.o + +CFLAGS+= -DBB_VER='"$(VERSION)"' +CFLAGS+= -DBB_BT='"$(BUILDTIME)"' + +#all: busybox links +all: busybox + +busybox: $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o busybox $(OBJECTS) $(LIBRARIES) + #$(STRIP) + +links: + - ./busybox.mkll | sort >busybox.links + +clean: + - rm -f busybox busybox.links *~ *.o + +distclean: clean + - rm -f busybox + +force: diff --git a/README b/README new file mode 100644 index 000000000..7d4e0f304 --- /dev/null +++ b/README @@ -0,0 +1,27 @@ +Please see the LICENSE file for copyright information. + +Busybox is a multicall binary used to provide a minimal subset of +POSIX style commands and specialized functions. + +It is geared toward 'mini-systems' like boot floppies. Specifically +it is used in the Debian Rescue/Install system (which caused the +original busybox to be made), the Linux Router Project, and others. + +As of version 0.20 there is a version number. : ) +Also as of version 0.20, BB is modularized to allow an easy build of +only the BB parts you need, to reduce binary size. + +Edit the file busybox.def.h and comment out the parts you do not need +using C++ comments (//) + +After the build is complete a busybox.links file is generated to allow +you to easily make the sym/hard links to the busybox binary. + +Note the modular system is Makefile based, and purposly very +simplistic. It does no dependency checking. That is left for you +to figure out by trial and error. + +Please feed patches back to: + Dave Cinege +and: + Enrique Zanardi diff --git a/applets/busybox.c b/applets/busybox.c new file mode 100644 index 000000000..b7b2b6009 --- /dev/null +++ b/applets/busybox.c @@ -0,0 +1,230 @@ +#include "internal.h" +#include +#include +#include + +static int been_there_done_that = 0; + +static const struct Applet applets[] = { + +#ifdef BB_BUSYBOX //bin + {"busybox", busybox_main}, +#endif +#ifdef BB_BLOCK_DEVICE //sbin + {"block_device", block_device_main}, +#endif +#ifdef BB_CAT //bin + {"cat", cat_more_main}, +#endif +#ifdef BB_CHGRP //bin + {"chgrp", chgrp_main}, +#endif +#ifdef BB_CHMOD //bin + {"chmod", chmod_main}, +#endif +#ifdef BB_CHOWN //bin + {"chown", chown_main}, +#endif +#ifdef BB_CHROOT //sbin + {"chroot", chroot_main}, +#endif +#ifdef BB_CLEAR //usr/bin + {"clear", clear_main}, +#endif +#ifdef BB_CP //bin + {"cp", dyadic_main}, +#endif +#ifdef BB_DATE //bin + {"date", date_main}, +#endif +#ifdef BB_DD //bin + {"dd", dd_main}, +#endif +#ifdef BB_DF //bin + {"df", df_main}, +#endif +#ifdef BB_DMESG //bin + {"dmesg", dmesg_main}, +#endif +#ifdef BB_DUTMP //usr/sbin + {"dutmp", cat_more_main}, +#endif +#ifdef BB_FALSE //bin + {"false", false_main}, +#endif +#ifdef BB_FDFLUSH //bin + {"fdflush", monadic_main}, +#endif +#ifdef BB_FIND //usr/bin + {"find", find_main}, +#endif +#ifdef BB_GREP //bin + {"grep", grep_main}, +#endif +#ifdef BB_HALT //sbin + {"halt", halt_main}, +#endif +#ifdef BB_INIT //sbin + {"init", init_main}, +#endif +#ifdef BB_KILL //bin + {"kill", kill_main}, +#endif +#ifdef BB_LENGTH //usr/bin + {"length", length_main}, +#endif +#ifdef BB_LN //bin + {"ln", dyadic_main}, +#endif +#ifdef BB_LOADKMAP //sbin + {"loadkmap", loadkmap_main}, +#endif +#ifdef BB_LOSETUP //sbin + {"losetup", losetup_main}, +#endif +#ifdef BB_LS //bin + {"ls", ls_main}, +#endif +#ifdef BB_MAKEDEVS //sbin + {"makedevs", makedevs_main}, +#endif +#ifdef BB_MATH //usr/bin + {"math", math_main}, +#endif +#ifdef BB_MKDIR //bin + {"mkdir", monadic_main}, +#endif +#ifdef BB_MKNOD //bin + {"mknod", mknod_main}, +#endif +#ifdef BB_MKSWAP //sbin + {"mkswap", mkswap_main}, +#endif +#ifdef BB_MNC //usr/bin + {"mnc", mnc_main}, +#endif +#ifdef BB_MORE //bin + {"more", cat_more_main}, +#endif +#ifdef BB_MOUNT //bin + {"mount", mount_main}, +#endif +#ifdef BB_MT //bin + {"mt", mt_main}, +#endif +#ifdef BB_MV //bin + {"mv", dyadic_main}, +#endif +#ifdef BB_PRINTF //usr/bin + {"printf", printf_main}, +#endif +#ifdef BB_PWD //bin + {"pwd", pwd_main}, +#endif +#ifdef BB_REBOOT //sbin + {"reboot", reboot_main}, +#endif +#ifdef BB_RM //bin + {"rm", rm_main}, +#endif +#ifdef BB_RMDIR //bin + {"rmdir", monadic_main}, +#endif +#ifdef BB_SLEEP //bin + {"sleep", sleep_main}, +#endif +#ifdef BB_TAR //bin + {"tar", tar_main}, +#endif +#ifdef BB_SWAPOFF //sbin + {"swapoff", monadic_main}, +#endif +#ifdef BB_SWAPON //sbin + {"swapon", monadic_main}, +#endif +#ifdef BB_SYNC //bin + {"sync", sync_main}, +#endif +#ifdef BB_TOUCH //usr/bin + {"touch", monadic_main}, +#endif +#ifdef BB_TRUE //bin + {"true", true_main}, +#endif +#ifdef BB_UMOUNT //bin + {"umount", umount_main}, +#endif +#ifdef BB_UPDATE //sbin + {"update", update_main}, +#endif +#ifdef BB_ZCAT //bin + {"zcat", zcat_main}, + {"gunzip", zcat_main}, +#endif +#ifdef BB_GZIP //bin + {"gzip", gzip_main}, +#endif + {0} +}; + +int main(int argc, char **argv) +{ + char *s = argv[0]; + char *name = argv[0]; + const struct Applet *a = applets; + + while (*s != '\0') { + if (*s++ == '/') + name = s; + } + + while (a->name != 0) { + if (strcmp(name, a->name) == 0) { + int status; + + status = ((*(a->main)) (argc, argv)); + if (status < 0) { + fprintf(stderr, "%s: %s\n", a->name, strerror(errno)); + } + fprintf(stderr, "\n"); + exit(status); + } + a++; + } + return (busybox_main(argc, argv)); +} + + +int busybox_main(int argc, char **argv) +{ + argc--; + argv++; + + /* If we've already been here once, exit now */ + if (been_there_done_that == 1) + return -1; + been_there_done_that = 1; + + if (argc < 1) { + const struct Applet *a = applets; + fprintf(stderr, "BusyBox v%s (%s) multi-call binary -- GPL2\n", + BB_VER, BB_BT); + fprintf(stderr, "Usage: busybox [function] [arguments]...\n"); + fprintf(stderr, + "\n\tMost people will create a symlink to busybox for each\n" + "\tfunction name, and busybox will act like whatever you invoke it as.\n"); + fprintf(stderr, "\nCurrently defined functions:\n"); + + if (a->name != 0) { + fprintf(stderr, "%s", a->name); + a++; + } + while (a->name != 0) { + fprintf(stderr, ", %s", a->name); + a++; + } + fprintf(stderr, "\n\n"); + exit(-1); + } else + return (main(argc, argv)); +} diff --git a/applets/busybox.mkll b/applets/busybox.mkll new file mode 100755 index 000000000..e43a1ccb0 --- /dev/null +++ b/applets/busybox.mkll @@ -0,0 +1,17 @@ +#!/bin/sh +#Make busybox links list file + +DF="busybox.def.h" +MF="main.c" + +LIST="$(sed -n '/^#define/{s/^#define //p;}' $DF)" + + for def in ${LIST}; do + + set -- $(sed -n '/^#ifdef '$def'[ +| +].*/,/^#endif/{s/.*\/\///p; /^{ /{ s/^{ "//; s/",.*$//p;}; }' $MF) + path=$1; shift + + for n in $@; do + echo "$path/$n" + done + done diff --git a/archival/gzip.c b/archival/gzip.c new file mode 100644 index 000000000..6fd2e3971 --- /dev/null +++ b/archival/gzip.c @@ -0,0 +1,3231 @@ +/* gzip.c -- this is a stripped down version of gzip I put into busybox, it does + * only standard in to standard out with -9 compression. It also requires the + * zcat module for some important functions. + * + * Charles P. Wright + */ +#include "internal.h" +#ifdef BB_GZIP + +#ifndef BB_ZCAT +error: you need zcat to have gzip support! +#endif + +const char gzip_usage[] = "gzip\nignores all command line arguments\ncompress stdin to stdout with -9 compression\n"; + +/* gzip.h -- common declarations for all gzip modules + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if defined(__STDC__) || defined(PROTO) +# define OF(args) args +#else +# define OF(args) () +#endif + +#ifdef __STDC__ + typedef void *voidp; +#else + typedef char *voidp; +#endif + +/* I don't like nested includes, but the string and io functions are used + * too often + */ +#include +#if !defined(NO_STRING_H) || defined(STDC_HEADERS) +# include +# if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__) +# include +# endif +# define memzero(s, n) memset ((voidp)(s), 0, (n)) +#else +# include +# define strchr index +# define strrchr rindex +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) +# define memzero(s, n) bzero((s), (n)) +#endif + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#define local static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +/* Return codes from gzip */ +#define OK 0 +#define ERROR 1 +#define WARNING 2 + +/* Compression methods (see algorithm.doc) */ +#define STORED 0 +#define COMPRESSED 1 +#define PACKED 2 +#define LZHED 3 +/* methods 4 to 7 reserved */ +#define DEFLATED 8 +#define MAX_METHODS 9 +extern int method; /* compression method */ + +/* To save memory for 16 bit systems, some arrays are overlaid between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * inflate: window inbuf + * unpack: window inbuf prefix_len + * unlzh: left+right window c_table inbuf c_len + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +#ifdef DYN_ALLOC +# define EXTERN(type, array) extern type * near array +# define DECLARE(type, array, size) type * near array +# define ALLOC(type, array, size) { \ + array = (type*)fcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error("insufficient memory"); \ + } +# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} +#else +# define EXTERN(type, array) extern type array[] +# define DECLARE(type, array, size) type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +EXTERN(uch, inbuf); /* input buffer */ +EXTERN(uch, outbuf); /* output buffer */ +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ +#define tab_suffix window +#ifndef MAXSEG_64K +# define tab_prefix prev /* hash link (see deflate.c) */ +# define head (prev+WSIZE) /* hash head (see deflate.c) */ + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ +#else +# define tab_prefix0 prev +# define head tab_prefix1 + EXTERN(ush, tab_prefix0); /* prefix for even codes */ + EXTERN(ush, tab_prefix1); /* prefix for odd codes */ +#endif + +extern unsigned insize; /* valid bytes in inbuf */ +extern unsigned inptr; /* index of next byte to be processed in inbuf */ +extern unsigned outcnt; /* bytes in output buffer */ + +extern long bytes_in; /* number of input bytes */ +extern long bytes_out; /* number of output bytes */ +extern long header_bytes;/* number of bytes in gzip header */ + +#define isize bytes_in +/* for compatibility with old zip sources (to be cleaned) */ + +extern int ifd; /* input file descriptor */ +extern int ofd; /* output file descriptor */ +extern char ifname[]; /* input file name or "stdin" */ +extern char ofname[]; /* output file name or "stdout" */ +extern char *progname; /* program name */ + +extern long time_stamp; /* original time stamp (modification time) */ +extern long ifile_size; /* input file size, -1 for devices (debug only) */ + +typedef int file_t; /* Do not use stdio */ +#define NO_FILE (-1) /* in memory compression */ + + +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/ +#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +extern int decrypt; /* flag to turn on decryption */ +extern int exit_code; /* program exit code */ +extern int verbose; /* be verbose (-v) */ +extern int quiet; /* be quiet (-q) */ +extern int test; /* check .z file integrity */ +extern int to_stdout; /* output to stdout (-c) */ +extern int save_orig_name; /* set if original name must be saved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0)) +#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1)) + +/* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} +#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ + flush_window();} + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ if (outcnt < OUTBUFSIZ-2) { \ + outbuf[outcnt++] = (uch) ((w) & 0xff); \ + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ + } else { \ + put_byte((uch)((w) & 0xff)); \ + put_byte((uch)((ush)(w) >> 8)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ + +/* Macros for getting two-byte and four-byte header values */ +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#define WARN(msg) {if (!quiet) fprintf msg ; \ + if (exit_code == OK) exit_code = WARNING;} + +local void do_exit(int exitcode); + + /* in zip.c: */ +extern int zip OF((int in, int out)); +extern int file_read OF((char *buf, unsigned size)); + + /* in unzip.c */ +extern int unzip OF((int in, int out)); +extern int check_zipfile OF((int in)); + + /* in unpack.c */ +extern int unpack OF((int in, int out)); + + /* in unlzh.c */ +extern int unlzh OF((int in, int out)); + + /* in gzip.c */ +RETSIGTYPE abort_gzip OF((void)); + + /* in deflate.c */ +void lm_init OF((ush *flags)); +ulg deflate OF((void)); + + /* in trees.c */ +void ct_init OF((ush *attr, int *method)); +int ct_tally OF((int dist, int lc)); +ulg flush_block OF((char *buf, ulg stored_len, int eof)); + + /* in bits.c */ +void bi_init OF((file_t zipfile)); +void send_bits OF((int value, int length)); +unsigned bi_reverse OF((unsigned value, int length)); +void bi_windup OF((void)); +void copy_block OF((char *buf, unsigned len, int header)); +extern int (*read_buf) OF((char *buf, unsigned size)); + + /* in util.c: */ +extern int copy OF((int in, int out)); +extern ulg updcrc OF((uch *s, unsigned n)); +extern void clear_bufs OF((void)); +extern int fill_inbuf OF((int eof_ok)); +extern void flush_outbuf OF((void)); +extern void flush_window OF((void)); +extern void write_buf OF((int fd, voidp buf, unsigned cnt)); +extern char *strlwr OF((char *s)); +extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); +extern void error OF((char *m)); +extern void warn OF((char *a, char *b)); +extern void read_error OF((void)); +extern void write_error OF((void)); +extern void display_ratio OF((long num, long den, FILE *file)); +extern voidp xmalloc OF((unsigned int size)); + + /* in inflate.c */ +extern int inflate OF((void)); +/* lzw.h -- define the lzw functions. + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if !defined(OF) && defined(lint) +# include "gzip.h" +#endif + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#define BLOCK_MODE 0x80 +/* Block compression: if table is full and compression rate is dropping, + * clear the dictionary. + */ + +#define LZW_RESERVED 0x60 /* reserved bits */ + +#define CLEAR 256 /* flush the dictionary */ +#define FIRST (CLEAR+1) /* first free entry */ + +extern int maxbits; /* max bits per code for LZW */ +extern int block_mode; /* block compress mode -C compatible with 2.0 */ + +/* revision.h -- define the version number + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#define VERSION "1.2.4" +#define PATCHLEVEL 0 +#define REVDATE "18 Aug 93" + +/* This version does not support compression into old compress format: */ +#ifdef LZW +# undef LZW +#endif + +/* $Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + +/* $Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +#if defined(__OS2__) && !defined(OS2) +# define OS2 +#endif + +#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */ +# undef MSDOS +#endif + +#ifdef MSDOS +# ifdef __GNUC__ + /* DJGPP version 1.09+ on MS-DOS. + * The DJGPP 1.09 stat() function must be upgraded before gzip will + * fully work. + * No need for DIRENT, since defines POSIX_SOURCE which + * implies DIRENT. + */ +# define near +# else +# define MAXSEG_64K +# ifdef __TURBOC__ +# define NO_OFF_T +# ifdef __BORLANDC__ +# define DIRENT +# else +# define NO_UTIME +# endif +# else /* MSC */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# endif +# endif +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define NO_SIZE_CHECK +# define casemap(c) tolow(c) /* Force file names to lower case */ +# include +# define OS_CODE 0x00 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# if !defined(NO_ASM) && !defined(ASMV) +# define ASMV +# endif +#else +# define near +#endif + +#ifdef OS2 +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# ifdef OS2FAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) +# endif +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# include +# define OS_CODE 0x06 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# ifdef _MSC_VER +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define MAXSEG_64K +# undef near +# define near _near +# endif +# ifdef __EMX__ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define DIRENT +# define EXPAND(argc,argv) \ + {_response(&argc, &argv); _wildcard(&argc, &argv);} +# endif +# ifdef __BORLANDC__ +# define DIRENT +# endif +# ifdef __ZTC__ +# define NO_DIR +# define NO_UTIME_H +# include +# define EXPAND(argc,argv) \ + {response_expand(&argc, &argv);} +# endif +#endif + +#ifdef WIN32 /* Windows NT */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# include +# include +# ifdef NTFAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) /* Force file names to lower case */ +# endif +# define OS_CODE 0x0b +#endif + +#ifdef MSDOS +# ifdef __TURBOC__ +# include +# define DYN_ALLOC + /* Turbo C 2.0 does not accept static allocations of large arrays */ + void * fcalloc (unsigned items, unsigned size); + void fcfree (void *ptr); +# else /* MSC */ +# include +# define fcalloc(nitems,itemsize) halloc((long)(nitems),(itemsize)) +# define fcfree(ptr) hfree(ptr) +# endif +#else +# ifdef MAXSEG_64K +# define fcalloc(items,size) calloc((items),(size)) +# else +# define fcalloc(items,size) malloc((size_t)(items)*(size_t)(size)) +# endif +# define fcfree(ptr) free(ptr) +#endif + +#if defined(VAXC) || defined(VMS) +# define PATH_SEP ']' +# define PATH_SEP2 ':' +# define SUFFIX_SEP ';' +# define NO_MULTIPLE_DOTS +# define Z_SUFFIX "-gz" +# define RECORD_IO 1 +# define casemap(c) tolow(c) +# define OS_CODE 0x02 +# define OPTIONS_VAR "GZIP_OPT" +# define STDC_HEADERS +# define NO_UTIME +# define EXPAND(argc,argv) vms_expand_args(&argc,&argv); +# include +# define unlink delete +# ifdef VAXC +# define NO_FCNTL_H +# include +# endif +#endif + +#ifdef AMIGA +# define PATH_SEP2 ':' +# define STDC_HEADERS +# define OS_CODE 0x01 +# define ASMV +# ifdef __GNUC__ +# define DIRENT +# define HAVE_UNISTD_H +# else /* SASC */ +# define NO_STDIN_FSTAT +# define SYSDIR +# define NO_SYMLINK +# define NO_CHOWN +# define NO_FCNTL_H +# include /* for read() and write() */ +# define direct dirent + extern void _expand_args(int *argc, char ***argv); +# define EXPAND(argc,argv) _expand_args(&argc,&argv); +# undef O_BINARY /* disable useless --ascii option */ +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# ifndef STDC_HEADERS +# define STDC_HEADERS +# define HAVE_UNISTD_H +# define DIRENT +# endif +# define ASMV +# define OS_CODE 0x05 +# ifdef TOSFS +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define NO_SYMLINK +# endif +#endif + +#ifdef MACOS +# define PATH_SEP ':' +# define DYN_ALLOC +# define PROTO +# define NO_STDIN_FSTAT +# define NO_CHOWN +# define NO_UTIME +# define chmod(file, mode) (0) +# define OPEN(name, flags, mode) open(name, flags) +# define OS_CODE 0x07 +# ifdef MPW +# define isatty(fd) ((fd) <= 2) +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define PATH_SEP '>' +# define STDC_HEADERS +# define NO_MEMORY_H +# define NO_UTIME_H +# define NO_UTIME +# define NO_CHOWN +# define NO_STDIN_FSTAT +# define NO_SIZE_CHECK +# define NO_SYMLINK +# define RECORD_IO 1 +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define put_char(c) put_byte((c) & 0x7F) +# define get_char(c) ascii2pascii(get_byte()) +# define OS_CODE 0x0F /* temporary, subject to change */ +# ifdef SIGTERM +# undef SIGTERM /* We don't want a signal handler for SIGTERM */ +# endif +#endif + +#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */ +# define NOMEMCPY /* problem with overlapping copies */ +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifndef unix +# define NO_ST_INO /* don't rely on inode numbers */ +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef PATH_SEP +# define PATH_SEP '/' +#endif + +#ifndef casemap +# define casemap(c) (c) +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "GZIP" +#endif + +#ifndef Z_SUFFIX +# define Z_SUFFIX ".gz" +#endif + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + +#ifndef MAKE_LEGAL_NAME +# ifdef NO_MULTIPLE_DOTS +# define MAKE_LEGAL_NAME(name) make_simple_name(name) +# else +# define MAKE_LEGAL_NAME(name) +# endif +#endif + +#ifndef MIN_PART +# define MIN_PART 3 + /* keep at least MIN_PART chars between dots in a file name. */ +#endif + +#ifndef EXPAND +# define EXPAND(argc,argv) +#endif + +#ifndef RECORD_IO +# define RECORD_IO 0 +#endif + +#ifndef SET_BINARY_MODE +# define SET_BINARY_MODE(fd) +#endif + +#ifndef OPEN +# define OPEN(name, flags, mode) open(name, flags, mode) +#endif + +#ifndef get_char +# define get_char() get_byte() +#endif + +#ifndef put_char +# define put_char(c) put_byte(c) +#endif +/* bits.c -- output variable-length bit strings + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + + +/* + * PURPOSE + * + * Output variable-length bit strings. Compression can be done + * to a file or to memory. (The latter is not supported in this version.) + * + * DISCUSSION + * + * The PKZIP "deflate" file format interprets compressed file data + * as a sequence of bits. Multi-bit strings in the file may cross + * byte boundaries without restriction. + * + * The first bit of each byte is the low-order bit. + * + * The routines in this file allow a variable-length bit value to + * be output right-to-left (useful for literal values). For + * left-to-right output (useful for code strings from the tree routines), + * the bits must have been reversed first with bi_reverse(). + * + * For in-memory compression, the compressed bit stream goes directly + * into the requested output buffer. The input data is read in blocks + * by the mem_read() function. The buffer is limited to 64K on 16 bit + * machines. + * + * INTERFACE + * + * void bi_init (FILE *zipfile) + * Initialize the bit string routines. + * + * void send_bits (int value, int length) + * Write out a bit string, taking the source bits right to + * left. + * + * int bi_reverse (int value, int length) + * Reverse the bits of a bit string, taking the source bits left to + * right and emitting them right to left. + * + * void bi_windup (void) + * Write out any remaining bits in an incomplete byte. + * + * void copy_block(char *buf, unsigned len, int header) + * Copy a stored block to the zip file, storing first the length and + * its one's complement if requested. + * + */ + +#ifdef DEBUG +# include +#endif + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Local data used by the "bit string" routines. + */ + +local file_t zfile; /* output gzip file */ + +local unsigned short bi_buf; +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +local int bi_valid; +/* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +int (*read_buf) OF((char *buf, unsigned size)); +/* Current input function. Set to mem_read for in-memory compression */ + +#ifdef DEBUG + ulg bits_sent; /* bit length of the compressed data */ +#endif + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (zipfile) + file_t zipfile; /* output zip file, NO_FILE for in-memory compression */ +{ + zfile = zipfile; + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = 0L; +#endif + + /* Set the defaults for file compression. They are set by memcompress + * for in-memory compression. + */ + if (zfile != NO_FILE) { + read_buf = file_read; + } +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(value, length) + int value; /* value to send */ + int length; /* number of bits */ +{ +#ifdef DEBUG + Tracev((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + bits_sent += (ulg)length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (bi_valid > (int)Buf_size - length) { + bi_buf |= (value << bi_valid); + put_short(bi_buf); + bi_buf = (ush)value >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= value << bi_valid; + bi_valid += length; + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup() +{ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte(bi_buf); + } + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = (bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(buf, len, header) + char *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_short((ush)len); + put_short((ush)~len); +#ifdef DEBUG + bits_sent += 2*16; +#endif + } +#ifdef DEBUG + bits_sent += (ulg)len<<3; +#endif + while (len--) { +#ifdef CRYPT + int t; + if (key) zencode(*buf, t); +#endif + put_byte(*buf++); + } +} +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Identify new text as repetitions of old text within a fixed- + * length sliding window trailing behind the new text. + * + * DISCUSSION + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many info-zippers for bug reports and testing. + * + * REFERENCES + * + * APPNOTE.TXT documentation file in PKZIP 1.93a distribution. + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + * INTERFACE + * + * void lm_init (int pack_level, ush *flags) + * Initialize the "longest match" routines for a new file + * + * ulg deflate (void) + * Processes a new input file and return its compressed length. Sets + * the compressed length, crc, deflate flags and internal file + * attributes. + */ + +#include + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Configuration parameters + */ + +/* Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ + +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and + * window with tab_suffix. Check that we can do this: + */ +#if (WSIZE<<1) > (1< BITS-1 + error: cannot overlay head with tab_prefix1 +#endif + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + */ + +unsigned int near prev_length; +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + unsigned near strstart; /* start of string to insert */ + unsigned near match_start; /* start of matching string */ +local int eofile; /* flag set at end of input file */ +local unsigned lookahead; /* number of valid bytes ahead in window */ + +unsigned near max_chain_length; +/* To speed up deflation, hash chains are never searched beyond this length. + * A higher limit improves compression ratio but degrades the speed. + */ + +local unsigned int max_lazy_match; +/* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +#define max_insert_length max_lazy_match +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + +unsigned near good_match; +/* Use a faster search when the previous match is longer than this */ + + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + +typedef struct config { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; +} config; + +#ifdef FULL_SEARCH +# define nice_match MAX_MATCH +#else + int near nice_match; /* Stop searching when current match exceeds this */ +#endif + +local config configuration_table = +/* 9 */ {32, 258, 258, 4096}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +/* =========================================================================== + * Prototypes for local functions. + */ +local void fill_window OF((void)); + + int longest_match OF((IPos cur_match)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ +#endif + +#ifdef DEBUG +local void check_match OF((IPos start, IPos match, int length)); +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)<= 1 + */ +#ifndef ASMV +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +int longest_match(cur_match) + IPos cur_match; /* current match */ +{ + unsigned chain_length = max_chain_length; /* max hash chain length */ + register uch *scan = window + strstart; /* current string */ + register uch *match; /* matched string */ + register int len; /* length of current match */ + int best_len = prev_length; /* best match length so far */ + IPos limit = strstart > (IPos)MAX_DIST ? strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +#if HASH_BITS < 8 || MAX_MATCH != 258 + error: Code too clever +#endif + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register uch *strend = window + strstart + MAX_MATCH - 1; + register ush scan_start = *(ush*)scan; + register ush scan_end = *(ush*)(scan+best_len-1); +#else + register uch *strend = window + strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; +#endif + + /* Do not waste too much time if we already have a good match: */ + if (prev_length >= good_match) { + chain_length >>= 2; + } + Assert(strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(cur_match < strstart, "no future"); + match = window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ush*)(match+best_len-1) != scan_end || + *(ush*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + scan++, match++; + do { + } while (*(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= window+(unsigned)(window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ush*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} +#endif /* ASMV */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(start, match, length) + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (memcmp((char*)window + match, + (char*)window + start, length) != EQUAL) { + fprintf(stderr, + " start %d, match %d, length %d\n", + start, match, length); + error("invalid match"); + } + if (verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +local void fill_window() +{ + register unsigned n, m; + unsigned more = (unsigned)(window_size - (ulg)lookahead - (ulg)strstart); + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (strstart >= WSIZE+MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(window_size == (ulg)2*WSIZE, "no sliding with BIG_MEM"); + + memcpy((char*)window, (char*)window+WSIZE, (unsigned)WSIZE); + match_start -= WSIZE; + strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = prev[n]; + prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!eofile) { + n = read_buf((char*)window+strstart+lookahead, more); + if (n == 0 || n == (unsigned)EOF) { + eofile = 1; + } else { + lookahead += n; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(eof) \ + flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \ + (char*)NULL, (long)strstart - block_start, (eof)) + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate() +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ +#ifdef DEBUG + extern long isize; /* byte length of input file, for debug only */ +#endif + + /* Process the input block. */ + while (lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + prev_length = match_length, prev_match = match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && prev_length < max_lazy_match && + strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match (hash_head); + /* longest_match() sets match_start */ + if (match_length > lookahead) match_length = lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && strstart-match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (prev_length >= MIN_MATCH && match_length <= prev_length) { + + check_match(strstart-1, prev_match, prev_length); + + flush = ct_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + lookahead -= prev_length-1; + prev_length -= 2; + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH-1; + strstart++; + if (flush) FLUSH_BLOCK(0), block_start = strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c",window[strstart-1])); + if (ct_tally (0, window[strstart-1])) { + FLUSH_BLOCK(0), block_start = strstart; + } + strstart++; + lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + strstart++; + lookahead--; + } + Assert (strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) fill_window(); + } + if (match_available) ct_tally (0, window[strstart-1]); + + return FLUSH_BLOCK(1); /* eof */ +} +/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +/* Compress files with zip algorithm and 'compress' interface. + * See usage() and help() functions below for all options. + * Outputs: + * file.gz: compressed file with same mode, owner, and utimes + * or stdout with -c option or if stdin used as input. + * If the output file name had to be truncated, the original name is kept + * in the compressed file. + * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * + * For the meaning of all compilation flags, see comments in Makefile.in. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include +#include +#include + + /* configuration */ + +#ifdef NO_TIME_H +# include +#else +# include +#endif + +#ifndef NO_FCNTL_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#else + extern int errno; +#endif + +#if defined(DIRENT) +# include + typedef struct dirent dir_type; +# define NLENGTH(dirent) ((int)strlen((dirent)->d_name)) +# define DIR_OPT "DIRENT" +#else +# define NLENGTH(dirent) ((dirent)->d_namlen) +# ifdef SYSDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSDIR" +# else +# ifdef SYSNDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSNDIR" +# else +# ifdef NDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "NDIR" +# else +# define NO_DIR +# define DIR_OPT "NO_DIR" +# endif +# endif +# endif +#endif + +#ifndef NO_UTIME +# ifndef NO_UTIME_H +# include +# define TIME_OPT "UTIME" +# else +# ifdef HAVE_SYS_UTIME_H +# include +# define TIME_OPT "SYS_UTIME" +# else + struct utimbuf { + time_t actime; + time_t modtime; + }; +# define TIME_OPT "" +# endif +# endif +#else +# define TIME_OPT "NO_UTIME" +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +typedef RETSIGTYPE (*sig_type) OF((int)); + +#ifndef O_BINARY +# define O_BINARY 0 /* creation mode for open() */ +#endif + +#ifndef O_CREAT + /* Pure BSD system? */ +# include +# ifndef O_CREAT +# define O_CREAT FCREAT +# endif +# ifndef O_EXCL +# define O_EXCL FEXCL +# endif +#endif + +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif +#define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifdef NO_OFF_T + typedef long off_t; + off_t lseek OF((int fd, off_t offset, int whence)); +#endif + +/* Separator for file name parts (see shorten_name()) */ +#ifdef NO_MULTIPLE_DOTS +# define PART_SEP "-" +#else +# define PART_SEP "." +#endif + + /* global buffers */ + +DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA); +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); +DECLARE(ush, d_buf, DIST_BUFSIZE); +DECLARE(uch, window, 2L*WSIZE); +#ifndef MAXSEG_64K + DECLARE(ush, tab_prefix, 1L< + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + + +local int near extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local int near extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local int near extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#if LIT_BUFSIZE > INBUFSIZ + error cannot overlay l_buf and inbuf +#endif + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== + * Local data + */ + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +local ct_data near dyn_ltree[HEAP_SIZE]; /* literal and length tree */ +local ct_data near dyn_dtree[2*D_CODES+1]; /* distance tree */ + +local ct_data near static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + +local ct_data near static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +local ct_data near bl_tree[2*BL_CODES+1]; +/* Huffman tree for the bit lengths */ + +typedef struct tree_desc { + ct_data near *dyn_tree; /* the dynamic tree */ + ct_data near *static_tree; /* corresponding static tree or NULL */ + int near *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +local tree_desc near l_desc = +{dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; + +local tree_desc near d_desc = +{dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; + +local tree_desc near bl_desc = +{bl_tree, (ct_data near *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; + + +local ush near bl_count[MAX_BITS+1]; +/* number of codes at each bit length for an optimal tree */ + +local uch near bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +local int near heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ +local int heap_len; /* number of elements in the heap */ +local int heap_max; /* element of largest frequency */ +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + +local uch near depth[2*L_CODES+1]; +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local uch dist_code[512]; +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +local int near base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int near base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#define l_buf inbuf +/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */ + +/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */ + +local uch near flag_buf[(LIT_BUFSIZE/8)]; +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + +local unsigned last_lit; /* running index in l_buf */ +local unsigned last_dist; /* running index in d_buf */ +local unsigned last_flags; /* running index in flag_buf */ +local uch flags; /* current flags not yet saved in flag_buf */ +local uch flag_bit; /* current bit used in flags */ +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + +local ulg opt_len; /* bit length of current block with optimal trees */ +local ulg static_len; /* bit length of current block with static trees */ + +local ulg compressed_len; /* total bit length of compressed file */ + +local ulg input_len; /* total byte length of input file */ +/* input_len is for debugging only since we can get it by other means. */ + +ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */ +int *file_method; /* pointer to DEFLATE or STORE */ + +#ifdef DEBUG +extern ulg bits_sent; /* bit length of the compressed data */ +extern long isize; /* byte length of input file */ +#endif + +extern long block_start; /* window offset of current block */ +extern unsigned near strstart; /* window offset of current string */ + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void init_block OF((void)); +local void pqdownheap OF((ct_data near *tree, int k)); +local void gen_bitlen OF((tree_desc near *desc)); +local void gen_codes OF((ct_data near *tree, int max_code)); +local void build_tree OF((tree_desc near *desc)); +local void scan_tree OF((ct_data near *tree, int max_code)); +local void send_tree OF((ct_data near *tree, int max_code)); +local int build_bl_tree OF((void)); +local void send_all_trees OF((int lcodes, int dcodes, int blcodes)); +local void compress_block OF((ct_data near *ltree, ct_data near *dtree)); +local void set_file_type OF((void)); + + +#ifndef DEBUG +# define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(c, tree) \ + { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(attr, methodp) + ush *attr; /* pointer to internal file attribute */ + int *methodp; /* pointer to compression method */ +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + file_type = attr; + file_method = methodp; + compressed_len = input_len = 0L; + + if (static_dtree[0].Len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data near *)static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block() +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) bl_tree[n].Freq = 0; + + dyn_ltree[END_BLOCK].Freq = 1; + opt_len = static_len = 0L; + last_lit = last_dist = last_flags = 0; + flags = 0; flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = heap[SMALLEST]; \ + heap[SMALLEST] = heap[heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(tree, k) + ct_data near *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = heap[k]; + int j = k << 1; /* left son of k */ + while (j <= heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < heap_len && smaller(tree, heap[j+1], heap[j])) j++; + + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, heap[j])) break; + + /* Exchange v with the smallest son */ + heap[k] = heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(desc) + tree_desc near *desc; /* the tree descriptor */ +{ + ct_data near *tree = desc->dyn_tree; + int near *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data near *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap[heap_max]].Len = 0; /* root of the heap */ + + for (h = heap_max+1; h < HEAP_SIZE; h++) { + n = heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + opt_len += (ulg)f * (bits + xbits); + if (stree) static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (bl_count[bits] == 0) bits--; + bl_count[bits]--; /* move one leaf down the tree */ + bl_count[bits+1] += 2; /* move one overflow item as its brother */ + bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = bl_count[bits]; + while (n != 0) { + m = heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + opt_len += ((long)bits-(long)tree[m].Len)*(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code) + ct_data near *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + ct_data near *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len = 0, heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + heap[++heap_len] = max_code = n; + depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (heap_len < 2) { + int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0); + tree[new].Freq = 1; + depth[new] = 0; + opt_len--; if (stree) static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = heap_len/2; n >= 1; n--) pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = heap[SMALLEST]; /* m = node of next least frequency */ + + heap[--heap_max] = n; /* keep the nodes sorted by frequency */ + heap[--heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + depth[node] = (uch) (MAX(depth[n], depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (heap_len >= 2); + + heap[--heap_max] = heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc near *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data near *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +local void scan_tree (tree, max_code) + ct_data near *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) bl_tree[curlen].Freq++; + bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + bl_tree[REPZ_3_10].Freq++; + } else { + bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (tree, max_code) + ct_data near *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(REP_3_6, bl_tree); send_bits(count-3, 2); + + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); send_bits(count-3, 3); + + } else { + send_code(REPZ_11_138, bl_tree); send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree() +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree((ct_data near *)dyn_ltree, l_desc.max_code); + scan_tree((ct_data near *)dyn_dtree, d_desc.max_code); + + /* Build the bit length tree: */ + build_tree((tree_desc near *)(&bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(lcodes, dcodes, blcodes) + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", bits_sent)); + + send_tree((ct_data near *)dyn_ltree, lcodes-1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", bits_sent)); + + send_tree((ct_data near *)dyn_dtree, dcodes-1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", bits_sent)); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +ulg flush_block(buf, stored_len, eof) + char *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*file_type == (ush)UNKNOWN) set_file_type(); + + /* Construct the literal and distance trees */ + build_tree((tree_desc near *)(&l_desc)); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len)); + + build_tree((tree_desc near *)(&d_desc)); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (opt_len+3+7)>>3; + static_lenb = (static_len+3+7)>>3; + input_len += stored_len; /* for debugging only */ + + Trace((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, opt_len, static_lenb, static_len, stored_len, + last_lit, last_dist)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +#ifdef FORCE_METHOD +#else + if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) { +#endif + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (char*)0) error ("block vanished"); + + copy_block(buf, (unsigned)stored_len, 0); /* without header */ + compressed_len = stored_len << 3; + *file_method = STORED; + +#ifdef FORCE_METHOD +#else + } else if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK<<1)+eof, 3); /* send block type */ + compressed_len = (compressed_len + 3 + 7) & ~7L; + compressed_len += (stored_len + 4) << 3; + + copy_block(buf, (unsigned)stored_len, 1); /* with header */ + +#ifdef FORCE_METHOD +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits((STATIC_TREES<<1)+eof, 3); + compress_block((ct_data near *)static_ltree, (ct_data near *)static_dtree); + compressed_len += 3 + static_len; + } else { + send_bits((DYN_TREES<<1)+eof, 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block((ct_data near *)dyn_ltree, (ct_data near *)dyn_dtree); + compressed_len += 3 + opt_len; + } + Assert (compressed_len == bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + Assert (input_len == isize, "bad input size"); + bi_windup(); + compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", compressed_len>>3, + compressed_len-7*eof)); + + return compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (dist, lc) + int dist; /* distance of matched string */ + int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + l_buf[last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + dyn_dtree[d_code(dist)].Freq++; + + d_buf[last_dist++] = (ush)dist; + flags |= flag_bit; + } + flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((last_lit & 7) == 0) { + flag_buf[last_flags++] = flags; + flags = 0, flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)last_lit*8L; + ulg in_length = (ulg)strstart-block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)dyn_dtree[dcode].Freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + last_lit, last_dist, in_length, out_length, + 100L - out_length*100L/in_length)); + if (last_dist < last_lit/2 && out_length < in_length/2) return 1; + } + return (last_lit == LIT_BUFSIZE-1 || last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(ltree, dtree) + ct_data near *ltree; /* literal tree */ + ct_data near *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (last_lit != 0) do { + if ((lx & 7) == 0) flag = flag_buf[fx++]; + lc = l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < last_lit); + + send_code(END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_file_type() +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += dyn_ltree[n++].Freq; + *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII; + if (*file_type == BINARY && translate_eol) { + warn("-l used on binary file", ""); + } +} +/* util.c -- utility functions for gzip support + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifndef NO_FCNTL_H +# include +#endif + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#else + extern int errno; +#endif + +extern ulg crc_32_tab[]; /* crc table, defined below */ + +/* =========================================================================== + * Copy input to output unchanged: zcat == cat with --force. + * IN assertion: insize bytes have already been read in inbuf. + */ +int copy(in, out) + int in, out; /* input and output file descriptors */ +{ + errno = 0; + while (insize != 0 && (int)insize != EOF) { + write_buf(out, (char*)inbuf, insize); + bytes_out += insize; + insize = read(in, (char*)inbuf, INBUFSIZ); + } + if ((int)insize == EOF && errno != 0) { + read_error(); + } + bytes_in = bytes_out; + return OK; +} + +/* ======================================================================== + * Put string s in lower case, return s. + */ +char *strlwr(s) + char *s; +{ + char *t; + for (t = s; *t; t++) *t = tolow(*t); + return s; +} + +#if defined(NO_STRING_H) && !defined(STDC_HEADERS) + +/* Provide missing strspn and strcspn functions. */ + +# ifndef __STDC__ +# define const +# endif + +int strspn OF((const char *s, const char *accept)); +int strcspn OF((const char *s, const char *reject)); + +/* ======================================================================== + * Return the length of the maximum initial segment + * of s which contains only characters in accept. + */ +int strspn(s, accept) + const char *s; + const char *accept; +{ + register const char *p; + register const char *a; + register int count = 0; + + for (p = s; *p != '\0'; ++p) { + for (a = accept; *a != '\0'; ++a) { + if (*p == *a) break; + } + if (*a == '\0') return count; + ++count; + } + return count; +} + +/* ======================================================================== + * Return the length of the maximum inital segment of s + * which contains no characters from reject. + */ +int strcspn(s, reject) + const char *s; + const char *reject; +{ + register int count = 0; + + while (*s != '\0') { + if (strchr(reject, *s++) != NULL) return count; + ++count; + } + return count; +} + +#endif /* NO_STRING_H */ + +/* ======================================================================== + * Add an environment variable (if any) before argv, and update argc. + * Return the expanded environment variable to be freed later, or NULL + * if no options were added to argv. + */ +#define SEPARATOR " \t" /* separators in env variable */ + +char *add_envopt(argcp, argvp, env) + int *argcp; /* pointer to argc */ + char ***argvp; /* pointer to argv */ + char *env; /* name of environment variable */ +{ + char *p; /* running pointer through env variable */ + char **oargv; /* runs through old argv array */ + char **nargv; /* runs through new argv array */ + int oargc = *argcp; /* old argc */ + int nargc = 0; /* number of arguments in env variable */ + + env = (char*)getenv(env); + if (env == NULL) return NULL; + + p = (char*)xmalloc(strlen(env)+1); + env = strcpy(p, env); /* keep env variable intact */ + + for (p = env; *p; nargc++ ) { /* move through env */ + p += strspn(p, SEPARATOR); /* skip leading separators */ + if (*p == '\0') break; + + p += strcspn(p, SEPARATOR); /* find end of word */ + if (*p) *p++ = '\0'; /* mark it */ + } + if (nargc == 0) { + free(env); + return NULL; + } + *argcp += nargc; + /* Allocate the new argv array, with an extra element just in case + * the original arg list did not end with a NULL. + */ + nargv = (char**)calloc(*argcp+1, sizeof(char *)); + if (nargv == NULL) error("out of memory"); + oargv = *argvp; + *argvp = nargv; + + /* Copy the program name first */ + if (oargc-- < 0) error("argc<=0"); + *(nargv++) = *(oargv++); + + /* Then copy the environment args */ + for (p = env; nargc > 0; nargc--) { + p += strspn(p, SEPARATOR); /* skip separators */ + *(nargv++) = p; /* store start */ + while (*p++) ; /* skip over word */ + } + + /* Finally copy the old args and add a NULL (usual convention) */ + while (oargc--) *(nargv++) = *(oargv++); + *nargv = NULL; + return env; +} +/* ======================================================================== + * Display compression ratio on the given stream on 6 characters. + */ +void display_ratio(num, den, file) + long num; + long den; + FILE *file; +{ + long ratio; /* 1000 times the compression ratio */ + + if (den == 0) { + ratio = 0; /* no compression */ + } else if (den < 2147483L) { /* (2**31 -1)/1000 */ + ratio = 1000L*num/den; + } else { + ratio = num/(den/1000L); + } + if (ratio < 0) { + putc('-', file); + ratio = -ratio; + } else { + putc(' ', file); + } + fprintf(file, "%2ld.%1ld%%", ratio / 10L, ratio % 10L); +} + + +/* zip.c -- compress files to the gzip or pkzip format + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifndef NO_FCNTL_H +# include +#endif + +local ulg crc; /* crc on uncompressed file data */ +long header_bytes; /* number of bytes in gzip header */ + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + * The variables time_stamp and save_orig_name are initialized. + */ +int zip(in, out) + int in, out; /* input and output file descriptors */ +{ + uch flags = 0; /* general purpose bit flags */ + ush attr = 0; /* ascii/binary flag */ + ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + + ifd = in; + ofd = out; + outcnt = 0; + + /* Write the header to the gzip file. See algorithm.doc for the format */ + + method = DEFLATED; + put_byte(GZIP_MAGIC[0]); /* magic header */ + put_byte(GZIP_MAGIC[1]); + put_byte(DEFLATED); /* compression method */ + + put_byte(flags); /* general flags */ + put_long(time_stamp); + + /* Write deflated file to zip file */ + crc = updcrc(0, 0); + + bi_init(out); + ct_init(&attr, &method); + lm_init(&deflate_flags); + + put_byte((uch)deflate_flags); /* extra flags */ + put_byte(OS_CODE); /* OS identifier */ + + header_bytes = (long)outcnt; + + (void)deflate(); + + /* Write the crc and uncompressed size */ + put_long(crc); + put_long(isize); + header_bytes += 2*sizeof(long); + + flush_outbuf(); + return OK; +} + + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +int file_read(buf, size) + char *buf; + unsigned size; +{ + unsigned len; + + Assert(insize == 0, "inbuf not empty"); + + len = read(ifd, buf, size); + if (len == (unsigned)(-1) || len == 0) return (int)len; + + crc = updcrc((uch*)buf, len); + isize += (ulg)len; + return (int)len; +} +#endif diff --git a/archival/tar.c b/archival/tar.c new file mode 100644 index 000000000..03da96735 --- /dev/null +++ b/archival/tar.c @@ -0,0 +1,1425 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "tar" command, taken from sash. + * This allows creation, extraction, and listing of tar files. + * + * Permission to distribute this code under the GPL has been granted. + * Modified for busybox by Erik Andersen + */ + + +#include "internal.h" + +#ifdef BB_TAR + +const char tar_usage[] = +"Create, extract, or list files from a TAR file\n\n" +"usage: tar -[cxtvOf] [tarFileName] [FILE] ...\n" +"\tc=create, x=extract, t=list contents, v=verbose,\n" +"\tO=extract to stdout, f=tarfile or \"-\" for stdin\n"; + + + +#include +#include +#include +#include +#include +#include + +/* + * Tar file constants. + */ +#define TAR_BLOCK_SIZE 512 +#define TAR_NAME_SIZE 100 + + +/* + * The POSIX (and basic GNU) tar header format. + * This structure is always embedded in a TAR_BLOCK_SIZE sized block + * with zero padding. We only process this information minimally. + */ +typedef struct +{ + char name[TAR_NAME_SIZE]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checkSum[8]; + char typeFlag; + char linkName[TAR_NAME_SIZE]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devMajor[8]; + char devMinor[8]; + char prefix[155]; +} TarHeader; + +#define TAR_MAGIC "ustar" +#define TAR_VERSION "00" + +#define TAR_TYPE_REGULAR '0' +#define TAR_TYPE_HARD_LINK '1' +#define TAR_TYPE_SOFT_LINK '2' + + +/* + * Static data. + */ +static BOOL listFlag; +static BOOL extractFlag; +static BOOL createFlag; +static BOOL verboseFlag; +static BOOL tostdoutFlag; + +static BOOL inHeader; +static BOOL badHeader; +static BOOL errorFlag; +static BOOL skipFileFlag; +static BOOL warnedRoot; +static BOOL eofFlag; +static long dataCc; +static int outFd; +static char outName[TAR_NAME_SIZE]; + + +/* + * Static data associated with the tar file. + */ +static const char * tarName; +static int tarFd; +static dev_t tarDev; +static ino_t tarInode; + + +/* + * Local procedures to restore files from a tar file. + */ +static void readTarFile(int fileCount, char ** fileTable); +static void readData(const char * cp, int count); +static void createPath(const char * name, int mode); +static long getOctal(const char * cp, int len); + +static void readHeader(const TarHeader * hp, + int fileCount, char ** fileTable); + + +/* + * Local procedures to save files into a tar file. + */ +static void saveFile(const char * fileName, BOOL seeLinks); + +static void saveRegularFile(const char * fileName, + const struct stat * statbuf); + +static void saveDirectory(const char * fileName, + const struct stat * statbuf); + +static BOOL wantFileName(const char * fileName, + int fileCount, char ** fileTable); + +static void writeHeader(const char * fileName, + const struct stat * statbuf); + +static void writeTarFile(int fileCount, char ** fileTable); +static void writeTarBlock(const char * buf, int len); +static BOOL putOctal(char * cp, int len, long value); +extern const char * modeString(int mode); +extern const char * timeString(time_t timeVal); +extern int fullWrite(int fd, const char * buf, int len); +extern int fullRead(int fd, char * buf, int len); + + +extern int +tar_main(struct FileInfo *unused, int argc, char ** argv) +{ + const char * options; + + argc--; + argv++; + + if (argc < 1) + { + fprintf(stderr, "%s", tar_usage); + return 1; + } + + + errorFlag = FALSE; + extractFlag = FALSE; + createFlag = FALSE; + listFlag = FALSE; + verboseFlag = FALSE; + tostdoutFlag = FALSE; + tarName = NULL; + tarDev = 0; + tarInode = 0; + tarFd = -1; + + /* + * Parse the options. + */ + options = *argv++; + argc--; + + if (**argv == '-') { + for (; *options; options++) + { + switch (*options) + { + case 'f': + if (tarName != NULL) + { + fprintf(stderr, "Only one 'f' option allowed\n"); + + return 1; + } + + tarName = *argv++; + argc--; + + break; + + case 't': + listFlag = TRUE; + break; + + case 'x': + extractFlag = TRUE; + break; + + case 'c': + createFlag = TRUE; + break; + + case 'v': + verboseFlag = TRUE; + break; + + case 'O': + tostdoutFlag = TRUE; + break; + + case '-': + break; + + default: + fprintf(stderr, "Unknown tar flag '%c'\n", *options); + + return 1; + } + } + } + + /* + * Validate the options. + */ + if (extractFlag + listFlag + createFlag != 1) + { + fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n"); + + return 1; + } + + /* + * Do the correct type of action supplying the rest of the + * command line arguments as the list of files to process. + */ + if (createFlag) + writeTarFile(argc, argv); + else + readTarFile(argc, argv); + if (errorFlag) + fprintf(stderr, "\n"); + return( errorFlag); +} + + +/* + * Read a tar file and extract or list the specified files within it. + * If the list is empty than all files are extracted or listed. + */ +static void +readTarFile(int fileCount, char ** fileTable) +{ + const char * cp; + int cc; + int inCc; + int blockSize; + char buf[BUF_SIZE]; + + skipFileFlag = FALSE; + badHeader = FALSE; + warnedRoot = FALSE; + eofFlag = FALSE; + inHeader = TRUE; + inCc = 0; + dataCc = 0; + outFd = -1; + blockSize = sizeof(buf); + cp = buf; + + /* + * Open the tar file for reading. + */ + if ( (tarName==NULL) || !strcmp( tarName, "-") ) { + tarFd = STDIN; + } + else + tarFd = open(tarName, O_RDONLY); + + if (tarFd < 0) + { + perror(tarName); + errorFlag = TRUE; + return; + } + + /* + * Read blocks from the file until an end of file header block + * has been seen. (A real end of file from a read is an error.) + */ + while (!eofFlag) + { + /* + * Read the next block of data if necessary. + * This will be a large block if possible, which we will + * then process in the small tar blocks. + */ + if (inCc <= 0) + { + cp = buf; + inCc = fullRead(tarFd, buf, blockSize); + + if (inCc < 0) + { + perror(tarName); + errorFlag=TRUE; + goto done; + } + + if (inCc == 0) + { + fprintf(stderr, + "Unexpected end of file from \"%s\"", + tarName); + errorFlag=TRUE; + goto done; + } + } + + /* + * If we are expecting a header block then examine it. + */ + if (inHeader) + { + readHeader((const TarHeader *) cp, fileCount, fileTable); + + cp += TAR_BLOCK_SIZE; + inCc -= TAR_BLOCK_SIZE; + + continue; + } + + /* + * We are currently handling the data for a file. + * Process the minimum of the amount of data we have available + * and the amount left to be processed for the file. + */ + cc = inCc; + + if (cc > dataCc) + cc = dataCc; + + readData(cp, cc); + + /* + * If the amount left isn't an exact multiple of the tar block + * size then round it up to the next block boundary since there + * is padding at the end of the file. + */ + if (cc % TAR_BLOCK_SIZE) + cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE); + + cp += cc; + inCc -= cc; + } + +done: + /* + * Close the tar file if needed. + */ + if ((tarFd >= 0) && (close(tarFd) < 0)) + perror(tarName); + + /* + * Close the output file if needed. + * This is only done here on a previous error and so no + * message is required on errors. + */ + if (tostdoutFlag==FALSE) { + if (outFd >= 0) + (void) close(outFd); + } +} + + +/* + * Examine the header block that was just read. + * This can specify the information for another file, or it can mark + * the end of the tar file. + */ +static void +readHeader(const TarHeader * hp, int fileCount, char ** fileTable) +{ + int mode; + int uid; + int gid; + int checkSum; + long size; + time_t mtime; + const char * name; + int cc; + BOOL hardLink; + BOOL softLink; + + /* + * If the block is completely empty, then this is the end of the + * archive file. If the name is null, then just skip this header. + */ + name = hp->name; + + if (*name == '\0') + { + for (cc = TAR_BLOCK_SIZE; cc > 0; cc--) + { + if (*name++) + return; + } + + eofFlag = TRUE; + + return; + } + + /* + * There is another file in the archive to examine. + * Extract the encoded information and check it. + */ + mode = getOctal(hp->mode, sizeof(hp->mode)); + uid = getOctal(hp->uid, sizeof(hp->uid)); + gid = getOctal(hp->gid, sizeof(hp->gid)); + size = getOctal(hp->size, sizeof(hp->size)); + mtime = getOctal(hp->mtime, sizeof(hp->mtime)); + checkSum = getOctal(hp->checkSum, sizeof(hp->checkSum)); + + if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) + { + if (!badHeader) + fprintf(stderr, "Bad tar header, skipping\n"); + + badHeader = TRUE; + + return; + } + + badHeader = FALSE; + skipFileFlag = FALSE; + + /* + * Check for the file modes. + */ + hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) || + (hp->typeFlag == TAR_TYPE_HARD_LINK - '0')); + + softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) || + (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0')); + + /* + * Check for a directory or a regular file. + */ + if (name[strlen(name) - 1] == '/') + mode |= S_IFDIR; + else if ((mode & S_IFMT) == 0) + mode |= S_IFREG; + + /* + * Check for absolute paths in the file. + * If we find any, then warn the user and make them relative. + */ + if (*name == '/') + { + while (*name == '/') + name++; + + if (!warnedRoot) + { + fprintf(stderr, + "Absolute path detected, removing leading slashes\n"); + } + + warnedRoot = TRUE; + } + + /* + * See if we want this file to be restored. + * If not, then set up to skip it. + */ + if (!wantFileName(name, fileCount, fileTable)) + { + if (!hardLink && !softLink && S_ISREG(mode)) + { + inHeader = (size == 0); + dataCc = size; + } + + skipFileFlag = TRUE; + + return; + } + + /* + * This file is to be handled. + * If we aren't extracting then just list information about the file. + */ + if (!extractFlag) + { + if (verboseFlag) + { + printf("%s %3d/%-d %9ld %s %s", modeString(mode), + uid, gid, size, timeString(mtime), name); + } + else + printf("%s", name); + + if (hardLink) + printf(" (link to \"%s\")", hp->linkName); + else if (softLink) + printf(" (symlink to \"%s\")", hp->linkName); + else if (S_ISREG(mode)) + { + inHeader = (size == 0); + dataCc = size; + } + + printf("\n"); + + return; + } + + /* + * We really want to extract the file. + */ + if (verboseFlag) + printf("x %s\n", name); + + if (hardLink) + { + if (link(hp->linkName, name) < 0) + perror(name); + + return; + } + + if (softLink) + { +#ifdef S_ISLNK + if (symlink(hp->linkName, name) < 0) + perror(name); +#else + fprintf(stderr, "Cannot create symbolic links\n"); +#endif + return; + } + + /* + * If the file is a directory, then just create the path. + */ + if (S_ISDIR(mode)) + { + createPath(name, mode); + + return; + } + + /* + * There is a file to write. + * First create the path to it if necessary with a default permission. + */ + createPath(name, 0777); + + inHeader = (size == 0); + dataCc = size; + + /* + * Start the output file. + */ + if (tostdoutFlag==TRUE) + outFd = STDOUT; + else + outFd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); + + if (outFd < 0) + { + perror(name); + skipFileFlag = TRUE; + return; + } + + /* + * If the file is empty, then that's all we need to do. + */ + if (size == 0 && tostdoutFlag == FALSE) + { + (void) close(outFd); + outFd = -1; + } +} + + +/* + * Handle a data block of some specified size that was read. + */ +static void +readData(const char * cp, int count) +{ + /* + * Reduce the amount of data left in this file. + * If there is no more data left, then we need to read + * the header again. + */ + dataCc -= count; + + if (dataCc <= 0) + inHeader = TRUE; + + /* + * If we aren't extracting files or this file is being + * skipped then do nothing more. + */ + if (!extractFlag || skipFileFlag) + return; + + /* + * Write the data to the output file. + */ + if (fullWrite(outFd, cp, count) < 0) + { + perror(outName); + if (tostdoutFlag==FALSE) { + (void) close(outFd); + outFd = -1; + } + skipFileFlag = TRUE; + return; + } + + /* + * If the write failed, close the file and disable further + * writes to this file. + */ + if (dataCc <= 0 && tostdoutFlag==FALSE) + { + if (close(outFd)) + perror(outName); + + outFd = -1; + } +} + + +/* + * Write a tar file containing the specified files. + */ +static void +writeTarFile(int fileCount, char ** fileTable) +{ + struct stat statbuf; + + /* + * Make sure there is at least one file specified. + */ + if (fileCount <= 0) + { + fprintf(stderr, "No files specified to be saved\n"); + errorFlag=TRUE; + } + + /* + * Create the tar file for writing. + */ + if ( (tarName==NULL) || !strcmp( tarName, "-") ) { + tostdoutFlag = TRUE; + tarFd = STDOUT; + } + else + tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if (tarFd < 0) + { + perror(tarName); + errorFlag=TRUE; + return; + } + + /* + * Get the device and inode of the tar file for checking later. + */ + if (fstat(tarFd, &statbuf) < 0) + { + perror(tarName); + errorFlag = TRUE; + goto done; + } + + tarDev = statbuf.st_dev; + tarInode = statbuf.st_ino; + + /* + * Append each file name into the archive file. + * Follow symbolic links for these top level file names. + */ + while (!errorFlag && (fileCount-- > 0)) + { + saveFile(*fileTable++, FALSE); + } + + /* + * Now write an empty block of zeroes to end the archive. + */ + writeTarBlock("", 1); + + +done: + /* + * Close the tar file and check for errors if it was opened. + */ + if ( (tostdoutFlag==FALSE) && (tarFd >= 0) && (close(tarFd) < 0)) + perror(tarName); +} + + +/* + * Save one file into the tar file. + * If the file is a directory, then this will recursively save all of + * the files and directories within the directory. The seeLinks + * flag indicates whether or not we want to see symbolic links as + * they really are, instead of blindly following them. + */ +static void +saveFile(const char * fileName, BOOL seeLinks) +{ + int status; + int mode; + struct stat statbuf; + + if (verboseFlag) + printf("a %s\n", fileName); + + /* + * Check that the file name will fit in the header. + */ + if (strlen(fileName) >= TAR_NAME_SIZE) + { + fprintf(stderr, "%s: File name is too long\n", fileName); + + return; + } + + /* + * Find out about the file. + */ +#ifdef S_ISLNK + if (seeLinks) + status = lstat(fileName, &statbuf); + else +#endif + status = stat(fileName, &statbuf); + + if (status < 0) + { + perror(fileName); + + return; + } + + /* + * Make sure we aren't trying to save our file into itself. + */ + if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) + { + fprintf(stderr, "Skipping saving of archive file itself\n"); + + return; + } + + /* + * Check the type of file. + */ + mode = statbuf.st_mode; + + if (S_ISDIR(mode)) + { + saveDirectory(fileName, &statbuf); + + return; + } + + if (S_ISREG(mode)) + { + saveRegularFile(fileName, &statbuf); + + return; + } + + /* + * The file is a strange type of file, ignore it. + */ + fprintf(stderr, "%s: not a directory or regular file\n", fileName); +} + + +/* + * Save a regular file to the tar file. + */ +static void +saveRegularFile(const char * fileName, const struct stat * statbuf) +{ + BOOL sawEof; + int fileFd; + int cc; + int dataCount; + long fullDataCount; + char data[TAR_BLOCK_SIZE * 16]; + + /* + * Open the file for reading. + */ + fileFd = open(fileName, O_RDONLY); + + if (fileFd < 0) + { + perror(fileName); + + return; + } + + /* + * Write out the header for the file. + */ + writeHeader(fileName, statbuf); + + /* + * Write the data blocks of the file. + * We must be careful to write the amount of data that the stat + * buffer indicated, even if the file has changed size. Otherwise + * the tar file will be incorrect. + */ + fullDataCount = statbuf->st_size; + sawEof = FALSE; + + while (fullDataCount > 0) + { + /* + * Get the amount to write this iteration which is + * the minumum of the amount left to write and the + * buffer size. + */ + dataCount = sizeof(data); + + if (dataCount > fullDataCount) + dataCount = (int) fullDataCount; + + /* + * Read the data from the file if we haven't seen the + * end of file yet. + */ + cc = 0; + + if (!sawEof) + { + cc = fullRead(fileFd, data, dataCount); + + if (cc < 0) + { + perror(fileName); + + (void) close(fileFd); + errorFlag = TRUE; + + return; + } + + /* + * If the file ended too soon, complain and set + * a flag so we will zero fill the rest of it. + */ + if (cc < dataCount) + { + fprintf(stderr, + "%s: Short read - zero filling", + fileName); + + sawEof = TRUE; + } + } + + /* + * Zero fill the rest of the data if necessary. + */ + if (cc < dataCount) + memset(data + cc, 0, dataCount - cc); + + /* + * Write the buffer to the TAR file. + */ + writeTarBlock(data, dataCount); + + fullDataCount -= dataCount; + } + + /* + * Close the file. + */ + if ( (tostdoutFlag==FALSE) && close(fileFd) < 0) + fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno)); +} + + +/* + * Save a directory and all of its files to the tar file. + */ +static void +saveDirectory(const char * dirName, const struct stat * statbuf) +{ + DIR * dir; + struct dirent * entry; + BOOL needSlash; + char fullName[PATH_LEN]; + + /* + * Construct the directory name as used in the tar file by appending + * a slash character to it. + */ + strcpy(fullName, dirName); + strcat(fullName, "/"); + + /* + * Write out the header for the directory entry. + */ + writeHeader(fullName, statbuf); + + /* + * Open the directory. + */ + dir = opendir(dirName); + + if (dir == NULL) + { + fprintf(stderr, "Cannot read directory \"%s\": %s\n", + dirName, strerror(errno)); + + return; + } + + /* + * See if a slash is needed. + */ + needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/')); + + /* + * Read all of the directory entries and check them, + * except for the current and parent directory entries. + */ + while (!errorFlag && ((entry = readdir(dir)) != NULL)) + { + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) + { + continue; + } + + /* + * Build the full path name to the file. + */ + strcpy(fullName, dirName); + + if (needSlash) + strcat(fullName, "/"); + + strcat(fullName, entry->d_name); + + /* + * Write this file to the tar file, noticing whether or not + * the file is a symbolic link. + */ + saveFile(fullName, TRUE); + } + + /* + * All done, close the directory. + */ + closedir(dir); +} + + +/* + * Write a tar header for the specified file name and status. + * It is assumed that the file name fits. + */ +static void +writeHeader(const char * fileName, const struct stat * statbuf) +{ + long checkSum; + const unsigned char * cp; + int len; + TarHeader header; + + /* + * Zero the header block in preparation for filling it in. + */ + memset((char *) &header, 0, sizeof(header)); + + /* + * Fill in the header. + */ + strcpy(header.name, fileName); + + strncpy(header.magic, TAR_MAGIC, sizeof(header.magic)); + strncpy(header.version, TAR_VERSION, sizeof(header.version)); + + putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777); + putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); + putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); + putOctal(header.size, sizeof(header.size), statbuf->st_size); + putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); + + header.typeFlag = TAR_TYPE_REGULAR; + + /* + * Calculate and store the checksum. + * This is the sum of all of the bytes of the header, + * with the checksum field itself treated as blanks. + */ + memset(header.checkSum, ' ', sizeof(header.checkSum)); + + cp = (const unsigned char *) &header; + len = sizeof(header); + checkSum = 0; + + while (len-- > 0) + checkSum += *cp++; + + putOctal(header.checkSum, sizeof(header.checkSum), checkSum); + + /* + * Write the tar header. + */ + writeTarBlock((const char *) &header, sizeof(header)); +} + + +/* + * Write data to one or more blocks of the tar file. + * The data is always padded out to a multiple of TAR_BLOCK_SIZE. + * The errorFlag static variable is set on an error. + */ +static void +writeTarBlock(const char * buf, int len) +{ + int partialLength; + int completeLength; + char fullBlock[TAR_BLOCK_SIZE]; + + /* + * If we had a write error before, then do nothing more. + */ + if (errorFlag) + return; + + /* + * Get the amount of complete and partial blocks. + */ + partialLength = len % TAR_BLOCK_SIZE; + completeLength = len - partialLength; + + /* + * Write all of the complete blocks. + */ + if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength)) + { + perror(tarName); + + errorFlag = TRUE; + + return; + } + + /* + * If there are no partial blocks left, we are done. + */ + if (partialLength == 0) + return; + + /* + * Copy the partial data into a complete block, and pad the rest + * of it with zeroes. + */ + memcpy(fullBlock, buf + completeLength, partialLength); + memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength); + + /* + * Write the last complete block. + */ + if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE)) + { + perror(tarName); + + errorFlag = TRUE; + } +} + + +/* + * Attempt to create the directories along the specified path, except for + * the final component. The mode is given for the final directory only, + * while all previous ones get default protections. Errors are not reported + * here, as failures to restore files can be reported later. + */ +static void +createPath(const char * name, int mode) +{ + char * cp; + char * cpOld; + char buf[TAR_NAME_SIZE]; + + strcpy(buf, name); + + cp = strchr(buf, '/'); + + while (cp) + { + cpOld = cp; + cp = strchr(cp + 1, '/'); + + *cpOld = '\0'; + + if (mkdir(buf, cp ? 0777 : mode) == 0) + printf("Directory \"%s\" created\n", buf); + + *cpOld = '/'; + } +} + + +/* + * Read an octal value in a field of the specified width, with optional + * spaces on both sides of the number and with an optional null character + * at the end. Returns -1 on an illegal format. + */ +static long +getOctal(const char * cp, int len) +{ + long val; + + while ((len > 0) && (*cp == ' ')) + { + cp++; + len--; + } + + if ((len == 0) || !isOctal(*cp)) + return -1; + + val = 0; + + while ((len > 0) && isOctal(*cp)) + { + val = val * 8 + *cp++ - '0'; + len--; + } + + while ((len > 0) && (*cp == ' ')) + { + cp++; + len--; + } + + if ((len > 0) && *cp) + return -1; + + return val; +} + + +/* + * Put an octal string into the specified buffer. + * The number is zero and space padded and possibly null padded. + * Returns TRUE if successful. + */ +static BOOL +putOctal(char * cp, int len, long value) +{ + int tempLength; + char * tempString; + char tempBuffer[32]; + + /* + * Create a string of the specified length with an initial space, + * leading zeroes and the octal number, and a trailing null. + */ + tempString = tempBuffer; + + sprintf(tempString, " %0*lo", len - 2, value); + + tempLength = strlen(tempString) + 1; + + /* + * If the string is too large, suppress the leading space. + */ + if (tempLength > len) + { + tempLength--; + tempString++; + } + + /* + * If the string is still too large, suppress the trailing null. + */ + if (tempLength > len) + tempLength--; + + /* + * If the string is still too large, fail. + */ + if (tempLength > len) + return FALSE; + + /* + * Copy the string to the field. + */ + memcpy(cp, tempString, len); + + return TRUE; +} + + +/* + * See if the specified file name belongs to one of the specified list + * of path prefixes. An empty list implies that all files are wanted. + * Returns TRUE if the file is selected. + */ +static BOOL +wantFileName(const char * fileName, int fileCount, char ** fileTable) +{ + const char * pathName; + int fileLength; + int pathLength; + + /* + * If there are no files in the list, then the file is wanted. + */ + if (fileCount == 0) + return TRUE; + + fileLength = strlen(fileName); + + /* + * Check each of the test paths. + */ + while (fileCount-- > 0) + { + pathName = *fileTable++; + + pathLength = strlen(pathName); + + if (fileLength < pathLength) + continue; + + if (memcmp(fileName, pathName, pathLength) != 0) + continue; + + if ((fileLength == pathLength) || + (fileName[pathLength] == '/')) + { + return TRUE; + } + } + + return FALSE; +} + + + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char * +modeString(int mode) +{ + static char buf[12]; + + strcpy(buf, "----------"); + + /* + * Fill in the file type. + */ + if (S_ISDIR(mode)) + buf[0] = 'd'; + if (S_ISCHR(mode)) + buf[0] = 'c'; + if (S_ISBLK(mode)) + buf[0] = 'b'; + if (S_ISFIFO(mode)) + buf[0] = 'p'; +#ifdef S_ISLNK + if (S_ISLNK(mode)) + buf[0] = 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + buf[0] = 's'; +#endif + + /* + * Now fill in the normal file permissions. + */ + if (mode & S_IRUSR) + buf[1] = 'r'; + if (mode & S_IWUSR) + buf[2] = 'w'; + if (mode & S_IXUSR) + buf[3] = 'x'; + if (mode & S_IRGRP) + buf[4] = 'r'; + if (mode & S_IWGRP) + buf[5] = 'w'; + if (mode & S_IXGRP) + buf[6] = 'x'; + if (mode & S_IROTH) + buf[7] = 'r'; + if (mode & S_IWOTH) + buf[8] = 'w'; + if (mode & S_IXOTH) + buf[9] = 'x'; + + /* + * Finally fill in magic stuff like suid and sticky text. + */ + if (mode & S_ISUID) + buf[3] = ((mode & S_IXUSR) ? 's' : 'S'); + if (mode & S_ISGID) + buf[6] = ((mode & S_IXGRP) ? 's' : 'S'); + if (mode & S_ISVTX) + buf[9] = ((mode & S_IXOTH) ? 't' : 'T'); + + return buf; +} + + +/* + * Get the time string to be used for a file. + * This is down to the minute for new files, but only the date for old files. + * The string is returned from a static buffer, and so is overwritten for + * each call. + */ +const char * +timeString(time_t timeVal) +{ + time_t now; + char * str; + static char buf[26]; + + time(&now); + + str = ctime(&timeVal); + + strcpy(buf, &str[4]); + buf[12] = '\0'; + + if ((timeVal > now) || (timeVal < now - 365*24*60*60L)) + { + strcpy(&buf[7], &str[20]); + buf[11] = '\0'; + } + + return buf; +} + + + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +int +fullWrite(int fd, const char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = write(fd, buf, len); + + if (cc < 0) + return -1; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +fullRead(int fd, char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = read(fd, buf, len); + + if (cc < 0) + return -1; + + if (cc == 0) + break; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + + +#endif +/* END CODE */ + + diff --git a/block_device.c b/block_device.c new file mode 100644 index 000000000..87e7209c0 --- /dev/null +++ b/block_device.c @@ -0,0 +1,64 @@ +#include "internal.h" +#include +#include +#include + +const char block_device_usage[] = "block_device mount-point"; + +static dev_t *my_device; +static char *my_device_name; + +int +match_mount(const struct FileInfo * i) { + if ( S_ISBLK(i->stat.st_mode) + && (i->stat.st_rdev == *my_device)) { + my_device_name=strdup(i->source); + return 1; + } else + return 0; +} + +extern int +block_device_main(struct FileInfo * i, int argc, char * * argv) +{ + char *device_name = block_device(argv[1],i); + if ( device_name == NULL ) + return -1; + printf("%s\n", device_name); + exit(0); +} + +char * block_device(const char *name, struct FileInfo *i) +{ + struct stat s; + char *buf; + int dinam=0; + + if ( stat(name, &s) ) return (char *) NULL; + if (!i) { + i=(struct FileInfo*)malloc(sizeof(struct FileInfo)); + dinam = 1; + } + memset((void *)i, 0, sizeof(struct FileInfo)); + my_device=(dev_t *)malloc(sizeof(dev_t)); + *my_device = s.st_dev; + my_device_name = NULL; + i->source = "/dev"; + i->stat = s; + i->processDirectoriesAfterTheirContents=1; + descend(i, match_mount); + if (dinam) free(i); + if ( my_device_name ) { + buf = strdup(my_device_name); + free(my_device); + free(my_device_name); + return buf; + } else { + fprintf( stderr + ,"Can't find special file for block device %d, %d.\n" + ,(int) *my_device >> 8 & 0xff + ,(int) *my_device & 0xff); + free(my_device); + return (char *) NULL; + } +} diff --git a/busybox.c b/busybox.c new file mode 100644 index 000000000..b7b2b6009 --- /dev/null +++ b/busybox.c @@ -0,0 +1,230 @@ +#include "internal.h" +#include +#include +#include + +static int been_there_done_that = 0; + +static const struct Applet applets[] = { + +#ifdef BB_BUSYBOX //bin + {"busybox", busybox_main}, +#endif +#ifdef BB_BLOCK_DEVICE //sbin + {"block_device", block_device_main}, +#endif +#ifdef BB_CAT //bin + {"cat", cat_more_main}, +#endif +#ifdef BB_CHGRP //bin + {"chgrp", chgrp_main}, +#endif +#ifdef BB_CHMOD //bin + {"chmod", chmod_main}, +#endif +#ifdef BB_CHOWN //bin + {"chown", chown_main}, +#endif +#ifdef BB_CHROOT //sbin + {"chroot", chroot_main}, +#endif +#ifdef BB_CLEAR //usr/bin + {"clear", clear_main}, +#endif +#ifdef BB_CP //bin + {"cp", dyadic_main}, +#endif +#ifdef BB_DATE //bin + {"date", date_main}, +#endif +#ifdef BB_DD //bin + {"dd", dd_main}, +#endif +#ifdef BB_DF //bin + {"df", df_main}, +#endif +#ifdef BB_DMESG //bin + {"dmesg", dmesg_main}, +#endif +#ifdef BB_DUTMP //usr/sbin + {"dutmp", cat_more_main}, +#endif +#ifdef BB_FALSE //bin + {"false", false_main}, +#endif +#ifdef BB_FDFLUSH //bin + {"fdflush", monadic_main}, +#endif +#ifdef BB_FIND //usr/bin + {"find", find_main}, +#endif +#ifdef BB_GREP //bin + {"grep", grep_main}, +#endif +#ifdef BB_HALT //sbin + {"halt", halt_main}, +#endif +#ifdef BB_INIT //sbin + {"init", init_main}, +#endif +#ifdef BB_KILL //bin + {"kill", kill_main}, +#endif +#ifdef BB_LENGTH //usr/bin + {"length", length_main}, +#endif +#ifdef BB_LN //bin + {"ln", dyadic_main}, +#endif +#ifdef BB_LOADKMAP //sbin + {"loadkmap", loadkmap_main}, +#endif +#ifdef BB_LOSETUP //sbin + {"losetup", losetup_main}, +#endif +#ifdef BB_LS //bin + {"ls", ls_main}, +#endif +#ifdef BB_MAKEDEVS //sbin + {"makedevs", makedevs_main}, +#endif +#ifdef BB_MATH //usr/bin + {"math", math_main}, +#endif +#ifdef BB_MKDIR //bin + {"mkdir", monadic_main}, +#endif +#ifdef BB_MKNOD //bin + {"mknod", mknod_main}, +#endif +#ifdef BB_MKSWAP //sbin + {"mkswap", mkswap_main}, +#endif +#ifdef BB_MNC //usr/bin + {"mnc", mnc_main}, +#endif +#ifdef BB_MORE //bin + {"more", cat_more_main}, +#endif +#ifdef BB_MOUNT //bin + {"mount", mount_main}, +#endif +#ifdef BB_MT //bin + {"mt", mt_main}, +#endif +#ifdef BB_MV //bin + {"mv", dyadic_main}, +#endif +#ifdef BB_PRINTF //usr/bin + {"printf", printf_main}, +#endif +#ifdef BB_PWD //bin + {"pwd", pwd_main}, +#endif +#ifdef BB_REBOOT //sbin + {"reboot", reboot_main}, +#endif +#ifdef BB_RM //bin + {"rm", rm_main}, +#endif +#ifdef BB_RMDIR //bin + {"rmdir", monadic_main}, +#endif +#ifdef BB_SLEEP //bin + {"sleep", sleep_main}, +#endif +#ifdef BB_TAR //bin + {"tar", tar_main}, +#endif +#ifdef BB_SWAPOFF //sbin + {"swapoff", monadic_main}, +#endif +#ifdef BB_SWAPON //sbin + {"swapon", monadic_main}, +#endif +#ifdef BB_SYNC //bin + {"sync", sync_main}, +#endif +#ifdef BB_TOUCH //usr/bin + {"touch", monadic_main}, +#endif +#ifdef BB_TRUE //bin + {"true", true_main}, +#endif +#ifdef BB_UMOUNT //bin + {"umount", umount_main}, +#endif +#ifdef BB_UPDATE //sbin + {"update", update_main}, +#endif +#ifdef BB_ZCAT //bin + {"zcat", zcat_main}, + {"gunzip", zcat_main}, +#endif +#ifdef BB_GZIP //bin + {"gzip", gzip_main}, +#endif + {0} +}; + +int main(int argc, char **argv) +{ + char *s = argv[0]; + char *name = argv[0]; + const struct Applet *a = applets; + + while (*s != '\0') { + if (*s++ == '/') + name = s; + } + + while (a->name != 0) { + if (strcmp(name, a->name) == 0) { + int status; + + status = ((*(a->main)) (argc, argv)); + if (status < 0) { + fprintf(stderr, "%s: %s\n", a->name, strerror(errno)); + } + fprintf(stderr, "\n"); + exit(status); + } + a++; + } + return (busybox_main(argc, argv)); +} + + +int busybox_main(int argc, char **argv) +{ + argc--; + argv++; + + /* If we've already been here once, exit now */ + if (been_there_done_that == 1) + return -1; + been_there_done_that = 1; + + if (argc < 1) { + const struct Applet *a = applets; + fprintf(stderr, "BusyBox v%s (%s) multi-call binary -- GPL2\n", + BB_VER, BB_BT); + fprintf(stderr, "Usage: busybox [function] [arguments]...\n"); + fprintf(stderr, + "\n\tMost people will create a symlink to busybox for each\n" + "\tfunction name, and busybox will act like whatever you invoke it as.\n"); + fprintf(stderr, "\nCurrently defined functions:\n"); + + if (a->name != 0) { + fprintf(stderr, "%s", a->name); + a++; + } + while (a->name != 0) { + fprintf(stderr, ", %s", a->name); + a++; + } + fprintf(stderr, "\n\n"); + exit(-1); + } else + return (main(argc, argv)); +} diff --git a/busybox.def.h b/busybox.def.h new file mode 100644 index 000000000..ad6480a25 --- /dev/null +++ b/busybox.def.h @@ -0,0 +1,64 @@ +/* + * This file is parsed by sed. You MUST use single line comments. + * IE //#define BB_BLAH + */ + +//#define BB_BLOCK_DEVICE +#define BB_BUSYBOX +#define BB_CAT +#define BB_CHGRP +//#define BB_CHMOD +//#define BB_CHOWN +//#define BB_CHROOT +//#define BB_CLEAR +//#define BB_CP +//#define BB_DATE +//#define BB_DD +//#define BB_DESCEND +//#define BB_DF +//#define BB_DMESG +//#define BB_DUTMP +//#define BB_DYADIC +//#define BB_FALSE +//#define BB_FDFLUSH +//#define BB_FIND +//#define BB_FINDMOUNT +//#define BB_GREP +////#define BB_HALT +//#define BB_INIT +//#define BB_KILL +////#define BB_LENGTH +//#define BB_LN +//#define BB_LOADKMAP +////#define BB_LOSETUP +//#define BB_LS +//#define BB_MAIN +//#define BB_MAKEDEVS +////#define BB_MATH +//#define BB_MKDIR +//#define BB_MKNOD +////#define BB_MKSWAP +//#define BB_MNC +//#define BB_MONADIC +//#define BB_MORE +//#define BB_MOUNT +////#define BB_MT +//#define BB_MV +//#define BB_POSTPROCESS +//#define BB_PRINTF +//#define BB_PWD +//#define BB_REBOOT +//#define BB_RM +//#define BB_RMDIR +//#define BB_SLEEP +////#define BB_SWAPOFF +//#define BB_SWAPON +//#define BB_SYNC +//#define BB_TAR +//#define BB_TOUCH +//#define BB_TRUE +//#define BB_UMOUNT +//#define BB_UPDATE +//#define BB_UTILITY +//#define BB_ZCAT +//#define BB_GZIP diff --git a/busybox.mkll b/busybox.mkll new file mode 100755 index 000000000..e43a1ccb0 --- /dev/null +++ b/busybox.mkll @@ -0,0 +1,17 @@ +#!/bin/sh +#Make busybox links list file + +DF="busybox.def.h" +MF="main.c" + +LIST="$(sed -n '/^#define/{s/^#define //p;}' $DF)" + + for def in ${LIST}; do + + set -- $(sed -n '/^#ifdef '$def'[ +| +].*/,/^#endif/{s/.*\/\///p; /^{ /{ s/^{ "//; s/",.*$//p;}; }' $MF) + path=$1; shift + + for n in $@; do + echo "$path/$n" + done + done diff --git a/busybox.spec b/busybox.spec new file mode 100644 index 000000000..46bd7f484 --- /dev/null +++ b/busybox.spec @@ -0,0 +1,43 @@ +Name: busybox +Version: 0.29alpha +Release: 1 +Group: System/Utilities +Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary. +Copyright: GPL +Packager : Erik Andersen +Conflicts: fileutils grep shellutils +Buildroot: /tmp/%{Name}-%{Version} +Source: busybox-0.29a1.tar.gz + +%Description +BusyBox is a suite of "tiny" Unix utilities in a multi-call binary. It +provides a pretty complete environment that fits on a floppy or in a +ROM. Just add "ash" (Keith Almquists tiny Bourne shell clone) and "ae", +and a kernel and you have a full system. This is used on the Debian +install disk and in an internet router, and it makes a good environment +for a "rescue" disk or any small or embedded system. + +%Prep +%setup -q -n busybox + +%Build +make + +%Install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/bin +h=`cat busybox.links` + +for i in $h ; do + mkdir -p $RPM_BUILD_ROOT/`echo $i | sed -e 's/\(^.*\/\)\(.*\)/\1/g' ` + (cd $RPM_BUILD_ROOT/bin ; ln -s ln `echo $i | sed -e 's/\(^.*\/\)\(.*\)/\2/g' ` ); +done +rm -f $RPM_BUILD_ROOT/bin/ln +install -m 755 busybox $RPM_BUILD_ROOT/bin/ln + +%Clean +rm -rf $RPM_BUILD_ROOT + +%Files +%defattr(-,root,root) +/ diff --git a/busybox_functions.h b/busybox_functions.h new file mode 100644 index 000000000..61fc48438 --- /dev/null +++ b/busybox_functions.h @@ -0,0 +1,11 @@ +#ifndef __BUSYBOX_FUNCTIONS_H__ +#define __BUSYBOX_FUNCTIONS_H__ + +int +mkswap(char *device_name, int pages, int check); +/* pages = 0 for autodetection */ + +int +fdflush(char *filename); + +#endif /* __BUSYBOX_FUNCTIONS_H__ */ diff --git a/cat.c b/cat.c new file mode 100644 index 000000000..12faf55ab --- /dev/null +++ b/cat.c @@ -0,0 +1,54 @@ +/* + * Mini Cat implementation for busybox + * + * Copyright (C) 1998 by Erik Andersen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" +#include + +const char cat_usage[] = "[file ...]"; + +extern int cat_more_main(int argc, char **argv) +{ + int c; + FILE *file = stdin; + + if (argc < 2) { + fprintf(stderr, "Usage: %s %s", *argv, cat_usage); + return 1; + } + argc--; + argv++; + + while (argc-- > 0) { + file = fopen(*argv, "r"); + if (file == NULL) { + name_and_error(*argv); + return 1; + } + while ((c = getc(file)) != EOF) + putc(c, stdout); + fclose(file); + fflush(stdout); + + argc--; + argv++; + } + return 0; +} diff --git a/chgrp.c b/chgrp.c new file mode 100644 index 000000000..038c665dd --- /dev/null +++ b/chgrp.c @@ -0,0 +1,89 @@ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1998 by Erik Andersen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" +#include +#include + +const char chgrp_usage[] = "chgrp [OPTION]... GROUP FILE...\n" + "Change the group membership of each FILE to GROUP.\n" + "\n\tOptions:\n" "\t-R\tchange files and directories recursively\n"; + +int chgrp_main(int argc, char **argv) +{ + const char *cp; + int gid; + struct group *grp; + struct stat statBuf; + + if (argc < 2) { + fprintf(stderr, "Usage: %s %s", *argv, chgrp_usage); + return 1; + } + argc--; + argv++; + + cp = argv[1]; + if (isDecimal(*cp)) { + gid = 0; + while (isDecimal(*cp)) + gid = gid * 10 + (*cp++ - '0'); + if (*cp) { + fprintf(stderr, "Bad gid value\n"); + return -1; + } + } else { + grp = getgrnam(cp); + if (grp == NULL) { + fprintf(stderr, "Unknown group name\n"); + return -1; + } + gid = grp->gr_gid; + } + argc--; + argv++; + while (argc-- > 1) { + argv++; + if ((stat(*argv, &statBuf) < 0) || + (chown(*argv, statBuf.st_uid, gid) < 0)) { + perror(*argv); + } + } + return 1; +} + + + + + + + + + +#if 0 +int +recursive(const char *fileName, BOOL followLinks, const char *pattern, + int (*fileAction) (const char *fileName, + const struct stat * statbuf), + int (*dirAction) (const char *fileName, + const struct stat * statbuf)) + +#endif diff --git a/chmod.c b/chmod.c new file mode 100644 index 000000000..225c92d10 --- /dev/null +++ b/chmod.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include "internal.h" + +const char chmod_usage[] = "chmod [-R] mode file [file ...]\n" +"\nmode may be an octal integer representing the bit pattern for the\n" +"\tnew mode, or a symbolic value matching the pattern\n" +"\t[ugoa]{+|-|=}[rwxst] .\n" +"\t\tu:\tUser\n" +"\t\tg:\tGroup\n" +"\t\to:\tOthers\n" +"\t\ta:\tAll\n" +"\n" +"\n+:\tAdd privilege\n" +"\n-:\tRemove privilege\n" +"\n=:\tSet privilege\n" +"\n" +"\t\tr:\tRead\n" +"\t\tw:\tWrite\n" +"\t\tx:\tExecute\n" +"\t\ts:\tSet User ID\n" +"\t\tt:\t\"Sticky\" Text\n" +"\n" +"\tModes may be concatenated, as in \"u=rwx,g=rx,o=rx,-t,-s\n" +"\n" +"\t-R:\tRecursively change the mode of all files and directories\n" +"\t\tunder the argument directory."; + +int +parse_mode( + const char * s +,mode_t * or +,mode_t * and +,int * group_execute) +{ + /* [ugoa]{+|-|=}[rwxstl] */ + mode_t mode = 0; + mode_t groups = S_ISVTX; + char type; + char c; + + do { + for ( ; ; ) { + switch ( c = *s++ ) { + case '\0': + return -1; + case 'u': + groups |= S_ISUID|S_IRWXU; + continue; + case 'g': + groups |= S_ISGID|S_IRWXG; + continue; + case 'o': + groups |= S_IRWXO; + continue; + case 'a': + groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + continue; + case '+': + case '=': + case '-': + type = c; + if ( groups == S_ISVTX ) /* The default is "all" */ + groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + break; + default: + if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) { + *and = 0; + *or = strtol(--s, 0, 010); + return 0; + } + else + return -1; + } + break; + } + + while ( (c = *s++) != '\0' ) { + switch ( c ) { + case ',': + break; + case 'r': + mode |= S_IRUSR|S_IRGRP|S_IROTH; + continue; + case 'w': + mode |= S_IWUSR|S_IWGRP|S_IWOTH; + continue; + case 'x': + mode |= S_IXUSR|S_IXGRP|S_IXOTH; + continue; + case 's': + if ( group_execute != 0 && (groups & S_IRWXG) ) { + if ( *group_execute < 0 ) + return -1; + if ( type != '-' ) { + mode |= S_IXGRP; + *group_execute = 1; + } + } + mode |= S_ISUID|S_ISGID; + continue; + case 'l': + if ( *group_execute > 0 ) + return -1; + if ( type != '-' ) { + *and &= ~S_IXGRP; + *group_execute = -1; + } + mode |= S_ISGID; + groups |= S_ISGID; + continue; + case 't': + mode |= S_ISVTX; + continue; + default: + return -1; + } + break; + } + switch ( type ) { + case '=': + *and &= ~(groups); + /* fall through */ + case '+': + *or |= mode & groups; + break; + case '-': + *and &= ~(mode & groups); + *or &= *and; + break; + } + } while ( c == ',' ); + return 0; +} + +extern int +chmod_main(struct FileInfo * i, int argc, char * * argv) +{ + i->andWithMode = S_ISVTX|S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + i->orWithMode = 0; + + while ( argc >= 3 ) { + if ( parse_mode(argv[1], &i->orWithMode, &i->andWithMode, 0) + == 0 ) { + argc--; + argv++; + } + else if ( strcmp(argv[1], "-R") == 0 ) { + i->recursive = 1; + argc--; + argv++; + } + else + break; + } + + i->changeMode = 1; + i->complainInPostProcess = 1; + + return monadic_main(i, argc, argv); +} diff --git a/chown.c b/chown.c new file mode 100644 index 000000000..a611f92f1 --- /dev/null +++ b/chown.c @@ -0,0 +1,63 @@ +#include "internal.h" +#include +#include +#include +#include + +const char chown_usage[] = "chown [-R] user-name file [file ...]\n" +"\n\tThe group list is kept in the file /etc/groups.\n\n" +"\t-R:\tRecursively change the mode of all files and directories\n" +"\t\tunder the argument directory."; + +int +parse_user_name(const char * s, struct FileInfo * i) +{ + struct passwd * p; + char * dot = strchr(s, '.'); + + if (! dot ) + dot = strchr(s, ':'); + + if ( dot ) + *dot = '\0'; + + if ( (p = getpwnam(s)) == 0 ) { + fprintf(stderr, "%s: no such user.\n", s); + return 1; + } + i->userID = p->pw_uid; + + if ( dot ) { + struct group * g = getgrnam(++dot); + if ( g == 0 ) { + fprintf(stderr, "%s: no such group.\n", dot); + return 1; + } + i->groupID = g->gr_gid; + i->changeGroupID = 1; + } + return 0; +} + +extern int +chown_main(struct FileInfo * i, int argc, char * * argv) +{ + int status; + + while ( argc >= 3 && strcmp("-R", argv[1]) == 0 ) { + i->recursive = 1; + argc--; + argv++; + } + + if ( (status = parse_user_name(argv[1], i)) != 0 ) + return status; + + argv++; + argc--; + + i->changeUserID = 1; + i->complainInPostProcess = 1; + + return monadic_main(i, argc, argv); +} diff --git a/chroot.c b/chroot.c new file mode 100644 index 000000000..ca0bfcf3f --- /dev/null +++ b/chroot.c @@ -0,0 +1,32 @@ +#include "internal.h" +#include +#include + + +const char chroot_usage[] = "chroot directory [command]\n" + "Run a command with special root directory.\n"; + +extern int +chroot_main (struct FileInfo *i, int argc, char **argv) +{ + char *prog; + + if (chroot (argv[1])) + { + name_and_error ("cannot chroot to that directory"); + return 1; + } + if (argc > 2) + { + execvp (argv[2], argv + 2); + } + else + { + prog = getenv ("SHELL"); + if (!prog) + prog = "/bin/sh"; + execlp (prog, prog, NULL); + } + name_and_error ("cannot exec"); + return 1; +} diff --git a/clear.c b/clear.c new file mode 100644 index 000000000..21a890c9e --- /dev/null +++ b/clear.c @@ -0,0 +1,13 @@ +#include "internal.h" +#include + +const char clear_usage[] = "clear\n" +"\n" +"\tClears the screen.\n"; + +extern int +clear_main(struct FileInfo * i, int argc, char * * argv) +{ + printf("\033[H\033[J"); + return 0; +} diff --git a/console-tools/clear.c b/console-tools/clear.c new file mode 100644 index 000000000..21a890c9e --- /dev/null +++ b/console-tools/clear.c @@ -0,0 +1,13 @@ +#include "internal.h" +#include + +const char clear_usage[] = "clear\n" +"\n" +"\tClears the screen.\n"; + +extern int +clear_main(struct FileInfo * i, int argc, char * * argv) +{ + printf("\033[H\033[J"); + return 0; +} diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c new file mode 100644 index 000000000..0f092d193 --- /dev/null +++ b/console-tools/loadkmap.c @@ -0,0 +1,68 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include + + +const char loadkmap_usage[] = "loadkmap\n" +"\n" +"\tLoad a binary keyboard translation table from standard input.\n" +"\n"; + + +int +loadkmap_main(struct FileInfo * info, int argc, char * * argv) +{ + struct kbentry ke; + u_short *ibuff; + int i,j,fd,readsz,pos,ibuffsz=NR_KEYS * sizeof(u_short); + char flags[MAX_NR_KEYMAPS],magic[]="bkeymap",buff[7]; + + fd = open("/dev/tty0", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Error opening /dev/tty0: %s\n", strerror(errno)); + return 1; + } + + read(0,buff,7); + if (0 != strncmp(buff,magic,7)) { + fprintf(stderr, "This is not a valid binary keymap.\n"); + return 1; + } + + if ( MAX_NR_KEYMAPS != read(0,flags,MAX_NR_KEYMAPS) ) { + fprintf(stderr, "Error reading keymap flags: %s\n", strerror(errno)); + return 1; + } + + ibuff=(u_short *) malloc(ibuffsz); + if (!ibuff) { + fprintf(stderr, "Out of memory.\n"); + return 1; + } + + for(i=0; i + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" +#include + +const char cat_usage[] = "[file ...]"; + +extern int cat_more_main(int argc, char **argv) +{ + int c; + FILE *file = stdin; + + if (argc < 2) { + fprintf(stderr, "Usage: %s %s", *argv, cat_usage); + return 1; + } + argc--; + argv++; + + while (argc-- > 0) { + file = fopen(*argv, "r"); + if (file == NULL) { + name_and_error(*argv); + return 1; + } + while ((c = getc(file)) != EOF) + putc(c, stdout); + fclose(file); + fflush(stdout); + + argc--; + argv++; + } + return 0; +} diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c new file mode 100644 index 000000000..038c665dd --- /dev/null +++ b/coreutils/chgrp.c @@ -0,0 +1,89 @@ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1998 by Erik Andersen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" +#include +#include + +const char chgrp_usage[] = "chgrp [OPTION]... GROUP FILE...\n" + "Change the group membership of each FILE to GROUP.\n" + "\n\tOptions:\n" "\t-R\tchange files and directories recursively\n"; + +int chgrp_main(int argc, char **argv) +{ + const char *cp; + int gid; + struct group *grp; + struct stat statBuf; + + if (argc < 2) { + fprintf(stderr, "Usage: %s %s", *argv, chgrp_usage); + return 1; + } + argc--; + argv++; + + cp = argv[1]; + if (isDecimal(*cp)) { + gid = 0; + while (isDecimal(*cp)) + gid = gid * 10 + (*cp++ - '0'); + if (*cp) { + fprintf(stderr, "Bad gid value\n"); + return -1; + } + } else { + grp = getgrnam(cp); + if (grp == NULL) { + fprintf(stderr, "Unknown group name\n"); + return -1; + } + gid = grp->gr_gid; + } + argc--; + argv++; + while (argc-- > 1) { + argv++; + if ((stat(*argv, &statBuf) < 0) || + (chown(*argv, statBuf.st_uid, gid) < 0)) { + perror(*argv); + } + } + return 1; +} + + + + + + + + + +#if 0 +int +recursive(const char *fileName, BOOL followLinks, const char *pattern, + int (*fileAction) (const char *fileName, + const struct stat * statbuf), + int (*dirAction) (const char *fileName, + const struct stat * statbuf)) + +#endif diff --git a/coreutils/chmod.c b/coreutils/chmod.c new file mode 100644 index 000000000..225c92d10 --- /dev/null +++ b/coreutils/chmod.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include "internal.h" + +const char chmod_usage[] = "chmod [-R] mode file [file ...]\n" +"\nmode may be an octal integer representing the bit pattern for the\n" +"\tnew mode, or a symbolic value matching the pattern\n" +"\t[ugoa]{+|-|=}[rwxst] .\n" +"\t\tu:\tUser\n" +"\t\tg:\tGroup\n" +"\t\to:\tOthers\n" +"\t\ta:\tAll\n" +"\n" +"\n+:\tAdd privilege\n" +"\n-:\tRemove privilege\n" +"\n=:\tSet privilege\n" +"\n" +"\t\tr:\tRead\n" +"\t\tw:\tWrite\n" +"\t\tx:\tExecute\n" +"\t\ts:\tSet User ID\n" +"\t\tt:\t\"Sticky\" Text\n" +"\n" +"\tModes may be concatenated, as in \"u=rwx,g=rx,o=rx,-t,-s\n" +"\n" +"\t-R:\tRecursively change the mode of all files and directories\n" +"\t\tunder the argument directory."; + +int +parse_mode( + const char * s +,mode_t * or +,mode_t * and +,int * group_execute) +{ + /* [ugoa]{+|-|=}[rwxstl] */ + mode_t mode = 0; + mode_t groups = S_ISVTX; + char type; + char c; + + do { + for ( ; ; ) { + switch ( c = *s++ ) { + case '\0': + return -1; + case 'u': + groups |= S_ISUID|S_IRWXU; + continue; + case 'g': + groups |= S_ISGID|S_IRWXG; + continue; + case 'o': + groups |= S_IRWXO; + continue; + case 'a': + groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + continue; + case '+': + case '=': + case '-': + type = c; + if ( groups == S_ISVTX ) /* The default is "all" */ + groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + break; + default: + if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) { + *and = 0; + *or = strtol(--s, 0, 010); + return 0; + } + else + return -1; + } + break; + } + + while ( (c = *s++) != '\0' ) { + switch ( c ) { + case ',': + break; + case 'r': + mode |= S_IRUSR|S_IRGRP|S_IROTH; + continue; + case 'w': + mode |= S_IWUSR|S_IWGRP|S_IWOTH; + continue; + case 'x': + mode |= S_IXUSR|S_IXGRP|S_IXOTH; + continue; + case 's': + if ( group_execute != 0 && (groups & S_IRWXG) ) { + if ( *group_execute < 0 ) + return -1; + if ( type != '-' ) { + mode |= S_IXGRP; + *group_execute = 1; + } + } + mode |= S_ISUID|S_ISGID; + continue; + case 'l': + if ( *group_execute > 0 ) + return -1; + if ( type != '-' ) { + *and &= ~S_IXGRP; + *group_execute = -1; + } + mode |= S_ISGID; + groups |= S_ISGID; + continue; + case 't': + mode |= S_ISVTX; + continue; + default: + return -1; + } + break; + } + switch ( type ) { + case '=': + *and &= ~(groups); + /* fall through */ + case '+': + *or |= mode & groups; + break; + case '-': + *and &= ~(mode & groups); + *or &= *and; + break; + } + } while ( c == ',' ); + return 0; +} + +extern int +chmod_main(struct FileInfo * i, int argc, char * * argv) +{ + i->andWithMode = S_ISVTX|S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + i->orWithMode = 0; + + while ( argc >= 3 ) { + if ( parse_mode(argv[1], &i->orWithMode, &i->andWithMode, 0) + == 0 ) { + argc--; + argv++; + } + else if ( strcmp(argv[1], "-R") == 0 ) { + i->recursive = 1; + argc--; + argv++; + } + else + break; + } + + i->changeMode = 1; + i->complainInPostProcess = 1; + + return monadic_main(i, argc, argv); +} diff --git a/coreutils/chown.c b/coreutils/chown.c new file mode 100644 index 000000000..a611f92f1 --- /dev/null +++ b/coreutils/chown.c @@ -0,0 +1,63 @@ +#include "internal.h" +#include +#include +#include +#include + +const char chown_usage[] = "chown [-R] user-name file [file ...]\n" +"\n\tThe group list is kept in the file /etc/groups.\n\n" +"\t-R:\tRecursively change the mode of all files and directories\n" +"\t\tunder the argument directory."; + +int +parse_user_name(const char * s, struct FileInfo * i) +{ + struct passwd * p; + char * dot = strchr(s, '.'); + + if (! dot ) + dot = strchr(s, ':'); + + if ( dot ) + *dot = '\0'; + + if ( (p = getpwnam(s)) == 0 ) { + fprintf(stderr, "%s: no such user.\n", s); + return 1; + } + i->userID = p->pw_uid; + + if ( dot ) { + struct group * g = getgrnam(++dot); + if ( g == 0 ) { + fprintf(stderr, "%s: no such group.\n", dot); + return 1; + } + i->groupID = g->gr_gid; + i->changeGroupID = 1; + } + return 0; +} + +extern int +chown_main(struct FileInfo * i, int argc, char * * argv) +{ + int status; + + while ( argc >= 3 && strcmp("-R", argv[1]) == 0 ) { + i->recursive = 1; + argc--; + argv++; + } + + if ( (status = parse_user_name(argv[1], i)) != 0 ) + return status; + + argv++; + argc--; + + i->changeUserID = 1; + i->complainInPostProcess = 1; + + return monadic_main(i, argc, argv); +} diff --git a/coreutils/chroot.c b/coreutils/chroot.c new file mode 100644 index 000000000..ca0bfcf3f --- /dev/null +++ b/coreutils/chroot.c @@ -0,0 +1,32 @@ +#include "internal.h" +#include +#include + + +const char chroot_usage[] = "chroot directory [command]\n" + "Run a command with special root directory.\n"; + +extern int +chroot_main (struct FileInfo *i, int argc, char **argv) +{ + char *prog; + + if (chroot (argv[1])) + { + name_and_error ("cannot chroot to that directory"); + return 1; + } + if (argc > 2) + { + execvp (argv[2], argv + 2); + } + else + { + prog = getenv ("SHELL"); + if (!prog) + prog = "/bin/sh"; + execlp (prog, prog, NULL); + } + name_and_error ("cannot exec"); + return 1; +} diff --git a/coreutils/cp.c b/coreutils/cp.c new file mode 100644 index 000000000..078a57c56 --- /dev/null +++ b/coreutils/cp.c @@ -0,0 +1,89 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char cp_usage[] = "cp [-r] source-file destination-file\n" +"\t\tcp [-r] source-file [source-file ...] destination-directory\n" +"\n" +"\tCopy the source files to the destination.\n" +"\n" +"\t-r:\tRecursively copy all files and directories\n" +"\t\tunder the argument directory."; + +extern int +cp_fn(const struct FileInfo * i) +{ + int sourceFd; + int destinationFd; + const char * destination = i->destination; + struct stat destination_stat; + int status; + char buf[8192]; + char d[PATH_MAX]; + + if ( (i->stat.st_mode & S_IFMT) == S_IFDIR ) { + if ( mkdir(destination, i->stat.st_mode & ~S_IFMT) + != 0 && errno != EEXIST ) { + name_and_error(destination); + return 1; + } + return 0; + } + if ( (sourceFd = open(i->source, O_RDONLY)) < 0 ) { + name_and_error(i->source); + return 1; + } + if ( stat(destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) { + fprintf(stderr + ,"copy of %s to %s would copy file to itself.\n" + ,i->source + ,destination); + close(sourceFd); + return 1; + } + } + /* + * If the destination is a directory, create a file within it. + */ + if ( (destination_stat.st_mode & S_IFMT) == S_IFDIR ) { + destination = join_paths( + d + ,i->destination + ,&i->source[i->directoryLength]); + + if ( stat(destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) { + fprintf(stderr + ,"copy of %s to %s would copy file to itself.\n" + ,i->source + ,destination); + close(sourceFd); + return 1; + } + } + } + + destinationFd = creat(destination, i->stat.st_mode & 07777); + + while ( (status = read(sourceFd, buf, sizeof(buf))) > 0 ) { + if ( write(destinationFd, buf, status) != status ) { + name_and_error(destination); + close(sourceFd); + close(destinationFd); + return 1; + } + } + close(sourceFd); + close(destinationFd); + if ( status < 0 ) { + name_and_error(i->source); + return 1; + } + return 0; +} diff --git a/coreutils/date.c b/coreutils/date.c new file mode 100644 index 000000000..a52a9a4a8 --- /dev/null +++ b/coreutils/date.c @@ -0,0 +1,305 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + + +/* This 'date' command supports only 2 time setting formats, + all the GNU strftime stuff (its in libc, lets use it), + setting time using UTC and displaying int, as well as + an RFC 822 complient date output for shell scripting + mail commands */ + +const char date_usage[] = "date [-uR] [+FORMAT|+%f] [ [-s|-d] MMDDhhmm[[CC]YY]\n | [[[[CCYY.]MM.DD-]hh:mm[:ss]]]] ]"; + +static struct option const long_options[] = +{ + {"date", required_argument, NULL, 'd'}, + /* {"rfc-822", no_argument, NULL, 'R'}, + {"set", required_argument, NULL, 's'}, + {"uct", no_argument, NULL, 'u'}, + {"utc", no_argument, NULL, 'u'}, + {"universal", no_argument, NULL, 'u'}, */ + {NULL, 0, NULL, 0} +}; + + + +/* Input parsing code is always bulky - used heavy duty libc stuff as + much as possible, missed out a lot of bounds checking */ + +/* Default input handling to save suprising some people */ + +struct tm * +date_conv_time(struct tm *tm_time, const char *t_string) { + int nr; + + nr = sscanf(t_string, "%2d%2d%2d%2d%d", + &(tm_time->tm_mon), + &(tm_time->tm_mday), + &(tm_time->tm_hour), + &(tm_time->tm_min), + &(tm_time->tm_year)); + + if(nr < 4 || nr > 5) { + fprintf(stderr, "date: invalid date `%s'\n", t_string); + exit(1); + } + + /* correct for century - minor Y2K problem here? */ + if(tm_time->tm_year >= 1900) + tm_time->tm_year -= 1900; + /* adjust date */ + tm_time->tm_mon -= 1; + + return(tm_time); + +} + + +/* The new stuff for LRP */ + +struct tm * +date_conv_ftime(struct tm *tm_time, const char *t_string) { + struct tm itm_time, jtm_time, ktm_time, \ + ltm_time, mtm_time, ntm_time; + + itm_time = *tm_time; + jtm_time = *tm_time; + ktm_time = *tm_time; + ltm_time = *tm_time; + mtm_time = *tm_time; + ntm_time = *tm_time; + + /* Parse input and assign appropriately to tm_time */ + + if(sscanf(t_string, "%d:%d:%d", + &itm_time.tm_hour, + &itm_time.tm_min, + &itm_time.tm_sec) == 3 ) { + + *tm_time = itm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d:%d", + &jtm_time.tm_hour, + &jtm_time.tm_min) == 2) { + + *tm_time = jtm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d-%d:%d:%d", + &ktm_time.tm_mon, + &ktm_time.tm_mday, + &ktm_time.tm_hour, + &ktm_time.tm_min, + &ktm_time.tm_sec) == 5) { + + ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ktm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d-%d:%d", + <m_time.tm_mon, + <m_time.tm_mday, + <m_time.tm_hour, + <m_time.tm_min) == 4) { + + ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ltm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d", + &mtm_time.tm_year, + &mtm_time.tm_mon, + &mtm_time.tm_mday, + &mtm_time.tm_hour, + &mtm_time.tm_min, + &mtm_time.tm_sec) == 6) { + + mtm_time.tm_year -= 1900; /* Adjust years */ + mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = mtm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d.%d-%d:%d", + &ntm_time.tm_year, + &ntm_time.tm_mon, + &ntm_time.tm_mday, + &ntm_time.tm_hour, + &ntm_time.tm_min) == 5) { + ntm_time.tm_year -= 1900; /* Adjust years */ + ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ntm_time; + return(tm_time); + + } + + fprintf(stderr, "date: invalid date `%s'\n", t_string); + + exit(1); + +} + + +void +date_err(void) { + fprintf(stderr, "date: only one date argument can be given at a time.\n"); + exit(1); +} + +int +date_main(struct FileInfo * i, int argc, char * * argv) +{ + char *date_str = NULL; + char *date_fmt = NULL; + char *t_buff; + int set_time = 0; + int rfc822 = 0; + int utc = 0; + int use_arg = 0; + int n_args; + time_t tm; + struct tm tm_time; + char optc; + + /* Interpret command line args */ + + + while ((optc = getopt_long (argc, argv, "d:Rs:u", long_options, NULL)) + != EOF) { + switch (optc) { + case 0: + break; + + case 'R': + rfc822 = 1; + break; + + case 's': + set_time = 1; + if(date_str != NULL) date_err(); + date_str = optarg; + break; + + case 'u': + utc = 1; + if (putenv ("TZ=UTC0") != 0) { + fprintf(stderr,"date: memory exhausted\n"); + return(1); + } +#if LOCALTIME_CACHE + tzset (); +#endif break; + + case 'd': + use_arg = 1; + if(date_str != NULL) date_err(); + date_str = optarg; + break; + + default: + usage(date_usage); + break; + } + } + + + n_args = argc - optind; + + while (n_args--){ + switch(argv[optind][0]) { + case '+': + /* Date format strings */ + if(date_fmt != NULL) { + fprintf(stderr, "date: only one date format can be given.\n"); + return(1); + } + date_fmt = &argv[optind][1]; + break; + + case '\0': + break; + + default: + /* Anything left over must be a date string to set the time */ + set_time = 1; + if(date_str != NULL) date_err(); + date_str = argv[optind]; + break; + } + optind++; + } + + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + time(&tm); + memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); + /* Zero out fields - take her back to midnight!*/ + if(date_str != NULL) { + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + } + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if(date_str != NULL) { + + if(strchr(date_str, ':') != NULL) { + date_conv_ftime(&tm_time, date_str); + } else { + date_conv_time(&tm_time, date_str); + } + + /* Correct any day of week and day of year etc fields */ + tm = mktime(&tm_time); + if (tm < 0 ) { + fprintf(stderr, "date: invalid date `%s'\n", date_str); + exit(1); + } + + /* if setting time, set it */ + if(set_time) { + if( stime(&tm) < 0) { + fprintf(stderr, "date: can't set date.\n"); + exit(1); + } + } + } + + /* Display output */ + + /* Deal with format string */ + if(date_fmt == NULL) { + date_fmt = (rfc822 + ? (utc + ? "%a, %_d %b %Y %H:%M:%S GMT" + : "%a, %_d %b %Y %H:%M:%S %z") + : "%a %b %e %H:%M:%S %Z %Y"); + + } else if ( *date_fmt == '\0' ) { + /* Imitate what GNU 'date' does with NO format string! */ + printf ("\n"); + return(0); + } + + /* Handle special conversions */ + + if( strncmp( date_fmt, "%f", 2) == 0 ) { + date_fmt = "%Y.%m.%d-%H:%M:%S"; + } + + /* Print OUTPUT (after ALL that!) */ + t_buff = malloc(201); + strftime(t_buff, 200, date_fmt, &tm_time); + printf("%s\n", t_buff); + + return(0); + +} diff --git a/coreutils/dd.c b/coreutils/dd.c new file mode 100644 index 000000000..8f1b9d409 --- /dev/null +++ b/coreutils/dd.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "dd" command, originally taken from sash. + * + * Permission to distribute this code under the GPL has been granted. + * Majorly modified, and bugs fixed for busybox by Erik Andersen + */ + +#include "internal.h" +#ifdef BB_DD + +const char dd_usage[] = +"Copy a file, converting and formatting according to options\n\ +\n\ +usage: [if=name] [of=name] [bs=n] [count=n]\n\ +\tif=FILE\tread from FILE instead of stdin\n\ +\tof=FILE\twrite to FILE instead of stout\n\ +\tbs=n\tread and write N bytes at a time\n\ +\tcount=n\tcopy only n input blocks\n\ +\n\ +BYTES may be suffixed: by k for x1024, b for x512, and w for x2.\n"; + + +#include +#include +#include +#include +#include +#include + + +#define PAR_NONE 0 +#define PAR_IF 1 +#define PAR_OF 2 +#define PAR_BS 3 +#define PAR_COUNT 4 + + +typedef struct +{ + const char * name; + int value; +} PARAM; + + +static const PARAM params[] = +{ + {"if", PAR_IF}, + {"of", PAR_OF}, + {"bs", PAR_BS}, + {"count", PAR_COUNT}, + {NULL, PAR_NONE} +}; + + +static long getNum(const char * cp); + +extern int +dd_main (struct FileInfo *unused, int argc, char **argv) +{ + const char * str; + const PARAM * par; + const char * inFile; + const char * outFile; + char * cp; + int inFd; + int outFd; + int inCc=0; + int outCc; + int blockSize; + long count; + long intotal; + long outTotal; + unsigned char* buf; + unsigned char localBuf[BUF_SIZE]; + + inFile = NULL; + outFile = NULL; + blockSize = 512; + count = 1; + + + while (--argc > 0) + { + str = *++argv; + cp = strchr(str, '='); + + if (cp == NULL) + { + fprintf(stderr, "Bad dd argument\n"); + goto usage; + } + + *cp++ = '\0'; + + for (par = params; par->name; par++) + { + if (strcmp(str, par->name) == 0) + break; + } + + switch (par->value) + { + case PAR_IF: + if (inFile) + { + fprintf(stderr, "Multiple input files illegal\n"); + goto usage; + } + + //fprintf(stderr, "if=%s\n", cp); + inFile = cp; + break; + + case PAR_OF: + if (outFile) + { + fprintf(stderr, "Multiple output files illegal\n"); + goto usage; + } + + //fprintf(stderr, "of=%s\n", cp); + outFile = cp; + break; + + case PAR_BS: + blockSize = getNum(cp); + //fprintf(stderr, "bs=%d\n", blockSize); + + if (blockSize <= 0) + { + fprintf(stderr, "Bad block size value\n"); + goto usage; + } + + break; + + case PAR_COUNT: + count = getNum(cp); + //fprintf(stderr, "count=%ld\n", count); + + if (count < 0) + { + fprintf(stderr, "Bad count value\n"); + goto usage; + } + + break; + + default: + goto usage; + } + } + + buf = localBuf; + + if (blockSize > sizeof(localBuf)) + { + buf = malloc(blockSize); + + if (buf == NULL) + { + fprintf(stderr, "Cannot allocate buffer\n"); + return 1; + } + } + + intotal = 0; + outTotal = 0; + + if (inFile == NULL) + inFd = STDIN; + else + inFd = open(inFile, 0); + + if (inFd < 0) + { + perror(inFile); + + if (buf != localBuf) + free(buf); + + return 1; + } + + if (outFile == NULL) + outFd = STDOUT; + else + outFd = creat(outFile, 0666); + + if (outFd < 0) + { + perror(outFile); + close(inFd); + + if (buf != localBuf) + free(buf); + + return 1; + } + + while ( outTotal < count*blockSize ) + { + inCc = read(inFd, buf, blockSize); + if (inCc < 0) { + perror(inFile); + goto cleanup; + } + //fprintf(stderr, "read in =%d\n", inCc); + intotal += inCc; + cp = buf; + + + while ( intotal > outTotal ) + { + if (outTotal+inCc > count*blockSize) + inCc=count*blockSize-outTotal; + outCc = write(outFd, cp, inCc); + if (outCc < 0) + { + perror(outFile); + goto cleanup; + } + //fprintf(stderr, "wrote out =%d\n", outCc); + + inCc -= outCc; + cp += outCc; + outTotal += outCc; + //fprintf(stderr, "outTotal=%ld\n", outTotal); + } + } + + if (inCc < 0) + perror(inFile); + +cleanup: + close(inFd); + + if (close(outFd) < 0) + perror(outFile); + + if (buf != localBuf) + free(buf); + + printf("%ld+%d records in\n", intotal / blockSize, + (intotal % blockSize) != 0); + + printf("%ld+%d records out\n", outTotal / blockSize, + (outTotal % blockSize) != 0); + return 0; +usage: + + fprintf(stderr, "%s", dd_usage); + return 1; +} + + +/* + * Read a number with a possible multiplier. + * Returns -1 if the number format is illegal. + */ +static long +getNum(const char * cp) +{ + long value; + + if (!isDecimal(*cp)) + return -1; + + value = 0; + + while (isDecimal(*cp)) + value = value * 10 + *cp++ - '0'; + + switch (*cp++) + { + case 'k': + value *= 1024; + break; + + case 'b': + value *= 512; + break; + + case 'w': + value *= 2; + break; + + case '\0': + return value; + + default: + return -1; + } + + if (*cp) + return -1; + + return value; +} + +#endif +/* END CODE */ + diff --git a/coreutils/df.c b/coreutils/df.c new file mode 100644 index 000000000..a0692afc5 --- /dev/null +++ b/coreutils/df.c @@ -0,0 +1,103 @@ +#include "internal.h" +#include +#include +#include +#include + +const char df_usage[] = "df [filesystem ...]\n" +"\n" +"\tPrint the filesystem space used and space available.\n"; + + +static int +df(const char * device, const char * mountPoint) +{ + struct statfs s; + long blocks_used; + long blocks_percent_used; + + if ( statfs(mountPoint, &s) != 0 ) { + name_and_error(mountPoint); + return 1; + } + + if ( s.f_blocks > 0 ) { + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = (long) + (blocks_used * 100.0 / (blocks_used + s.f_bavail) + 0.5); + +/* + printf( + "%-20s %7ld %7ld %7ld %5ld%% %s\n" + ,device + ,s.f_blocks + ,s.f_blocks - s.f_bfree + ,s.f_bavail + ,blocks_percent_used + ,mountPoint); +*/ + + printf( + "%-20s %7.0f %7.0f %7.0f %5ld%% %s\n" + ,device + ,s.f_blocks * (s.f_bsize / 1024.0) + ,(s.f_blocks - s.f_bfree) * (s.f_bsize / 1024.0) + ,s.f_bavail * (s.f_bsize / 1024.0) + ,blocks_percent_used + ,mountPoint); + + } + + return 0; +} + +extern int +df_main(struct FileInfo * i, int argc, char * * argv) +{ + static const char header[] = + "Filesystem 1024-blocks Used Available Capacity Mounted on\n"; + printf(header); + + if ( argc > 1 ) { + struct mntent * mountEntry; + int status; + + while ( argc > 1 ) { + if ( (mountEntry = findMountPoint(argv[1], "/etc/mtab")) == 0 + && (mountEntry = findMountPoint(argv[1], "/proc/mounts")) == 0 ) + { + fprintf(stderr, "%s: can't find mount point.\n" + ,argv[1]); + return 1; + } + status = df(mountEntry->mnt_fsname, mountEntry->mnt_dir); + if ( status != 0 ) + return status; + argc--; + argv++; + } + return 0; + } + else { + FILE * mountTable; + struct mntent * mountEntry; + + if ( (mountTable = setmntent("/etc/mtab", "r")) == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 + ) { + name_and_error("/etc/mtab"); + return 1; + } + + while ( (mountEntry = getmntent(mountTable)) != 0 ) { + int status = df( + mountEntry->mnt_fsname + ,mountEntry->mnt_dir); + if ( status != 0 ) + return status; + } + endmntent(mountTable); + } + + return 0; +} diff --git a/coreutils/length.c b/coreutils/length.c new file mode 100644 index 000000000..284bbfdf9 --- /dev/null +++ b/coreutils/length.c @@ -0,0 +1,13 @@ +#include "internal.h" +#include +#include +#include + +const char length_usage[] = "length string"; + +int +length_main(struct FileInfo * i, int argc, char * * argv) +{ + printf("%d\n", strlen(argv[1])); + return 0; +} diff --git a/coreutils/ln.c b/coreutils/ln.c new file mode 100644 index 000000000..3e87b579e --- /dev/null +++ b/coreutils/ln.c @@ -0,0 +1,52 @@ +#include "internal.h" +#include +#include +#include +#include + +const char ln_usage[] = "ln [-s] [-f] original-name additional-name\n" +"\n" +"\tAdd a new name that refers to the same file as \"original-name\"\n" +"\n" +"\t-s:\tUse a \"symbolic\" link, instead of a \"hard\" link.\n" +"\t-f:\tRemove existing destination files.\n"; + +int +ln_fn(const struct FileInfo * i) +{ + int status = 0; + char d[PATH_MAX]; + const char * destination = i->destination; + + if ( !i->makeSymbolicLink && (i->stat.st_mode & S_IFMT) == S_IFDIR ) { + fprintf(stderr, "Please use \"ln -s\" to link directories.\n"); + return 1; + } + + /* + * If the destination is a directory, create a file within it. + */ + if ( is_a_directory(i->destination) ) { + destination = join_paths( + d + ,i->destination + ,&i->source[i->directoryLength]); + } + + if ( i->force ) + status = ( unlink(destination) && errno != ENOENT ); + + if ( status == 0 ) { + if ( i->makeSymbolicLink ) + status = symlink(i->source, destination); + else + status = link(i->source, destination); + } + + if ( status != 0 ) { + name_and_error(destination); + return 1; + } + else + return 0; +} diff --git a/coreutils/ls.c b/coreutils/ls.c new file mode 100644 index 000000000..2566beea0 --- /dev/null +++ b/coreutils/ls.c @@ -0,0 +1,542 @@ +#include "internal.h" +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * 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. + */ + +/* + * To achieve a small memory footprint, this version of 'ls' doesn't do any + * file sorting, and only has the most essential command line switches + * (i.e. the ones I couldn't live without :-) All features which involve + * linking in substantial chunks of libc can be disabled. + * + * Although I don't really want to add new features to this program to + * keep it small, I *am* interested to receive bug fixes and ways to make + * it more portable. + * + * KNOWN BUGS: + * 1. messy output if you mix files and directories on the command line + * 2. ls -l of a directory doesn't give "total " header + * 3. ls of a symlink to a directory doesn't list directory contents + * 4. hidden files can make column width too large + * NON-OPTIMAL BEHAVIOUR: + * 1. autowidth reads directories twice + * 2. if you do a short directory listing without filetype characters + * appended, there's no need to stat each one + * PORTABILITY: + * 1. requires lstat (BSD) - how do you do it without? + */ + +#define FEATURE_USERNAME /* show username/groupnames (libc6 uses NSS) */ +#define FEATURE_TIMESTAMPS /* show file timestamps */ +#define FEATURE_AUTOWIDTH /* calculate terminal & column widths */ +#define FEATURE_FILETYPECHAR /* enable -p and -F */ + +#undef OP_BUF_SIZE 1024 /* leave undefined for unbuffered output */ + +#define TERMINAL_WIDTH 80 /* use 79 if your terminal has linefold bug */ +#define COLUMN_WIDTH 14 /* default if AUTOWIDTH not defined */ +#define COLUMN_GAP 2 /* includes the file type char, if present */ + +/************************************************************************/ + +#define HAS_REWINDDIR + +#if 1 /* FIXME libc 6 */ +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#ifdef FEATURE_USERNAME +#include +#include +#endif +#ifdef FEATURE_TIMESTAMPS +#include +#endif + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#ifdef FEATURE_FILETYPECHAR +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +#endif + +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +#define MODE1 "rwxrwxrwx" +#define MODE0 "---------" +#define SMODE1 "..s..s..t" +#define SMODE0 "..S..S..T" + +/* The 9 mode bits to test */ + +static const umode_t MBIT[] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH +}; + +/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ + +static const umode_t SBIT[] = { + 0, 0, S_ISUID, + 0, 0, S_ISGID, + 0, 0, S_ISVTX +}; + +#define FMT_AUTO 0 +#define FMT_LONG 1 /* one record per line, extended info */ +#define FMT_SINGLE 2 /* one record per line */ +#define FMT_ROWS 3 /* print across rows */ +#define FMT_COLUMNS 3 /* fill columns (same, since we don't sort) */ + +#define TIME_MOD 0 +#define TIME_CHANGE 1 +#define TIME_ACCESS 2 + +#define DISP_FTYPE 1 /* show character for file type */ +#define DISP_EXEC 2 /* show '*' if regular executable file */ +#define DISP_HIDDEN 4 /* show files starting . (except . and ..) */ +#define DISP_DOT 8 /* show . and .. */ +#define DISP_NUMERIC 16 /* numeric uid and gid */ +#define DISP_FULLTIME 32 /* show extended time display */ +#define DIR_NOLIST 64 /* show directory as itself, not contents */ +#define DISP_DIRNAME 128 /* show directory name (for internal use) */ +#define DIR_RECURSE 256 /* -R (not yet implemented) */ + +static unsigned char display_fmt = FMT_AUTO; +static unsigned short opts = 0; +static unsigned short column = 0; + +#ifdef FEATURE_AUTOWIDTH +static unsigned short terminal_width = 0, column_width = 0; +#else +#define terminal_width TERMINAL_WIDTH +#define column_width COLUMN_WIDTH +#endif + +#ifdef FEATURE_TIMESTAMPS +static unsigned char time_fmt = TIME_MOD; +#endif + +#define wr(data,len) fwrite(data, 1, len, stdout) + +static void writenum(long val, short minwidth) +{ + char scratch[20]; + + char *p = scratch + sizeof(scratch); + short len = 0; + short neg = (val < 0); + + if (neg) val = -val; + do + *--p = (val % 10) + '0', len++, val /= 10; + while (val); + if (neg) + *--p = '-', len++; + while (len < minwidth) + *--p = ' ', len++; + wr(p, len); + column += len; +} + +static void newline(void) +{ + if (column > 0) { + wr("\n", 1); + column = 0; + } +} + +static void tab(short col) +{ + static const char spaces[] = " "; + #define nspaces ((sizeof spaces)-1) /* null terminator! */ + + short n = col - column; + + if (n > 0) { + column = col; + while (n > nspaces) { + wr(spaces, nspaces); + n -= nspaces; + } + /* must be 1...(sizeof spaces) left */ + wr(spaces, n); + } + #undef nspaces +} + +#ifdef FEATURE_FILETYPECHAR +static char append_char(umode_t mode) +{ + if (!(opts & DISP_FTYPE)) + return '\0'; + if ((opts & DISP_EXEC) && S_ISREG(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +/** + ** + ** Display a file or directory as a single item + ** (in either long or short format) + ** + **/ + +static void list_single(const char *name, struct stat *info) +{ + char scratch[20]; + short len = strlen(name); +#ifdef FEATURE_FILETYPECHAR + char append = append_char(info->st_mode); +#endif + + if (display_fmt == FMT_LONG) { + umode_t mode = info->st_mode; + int i; + + scratch[0] = TYPECHAR(mode); + for (i=0; i<9; i++) + if (mode & SBIT[i]) + scratch[i+1] = (mode & MBIT[i]) + ? SMODE1[i] + : SMODE0[i]; + else + scratch[i+1] = (mode & MBIT[i]) + ? MODE1[i] + : MODE0[i]; + newline(); + wr(scratch, 10); + column=10; + writenum((long)info->st_nlink,(short)4); + fputs(" ", stdout); +#ifdef FEATURE_USERNAME + if (!(opts & DISP_NUMERIC)) { + struct passwd *pw = getpwuid(info->st_uid); + if (pw) + fputs(pw->pw_name, stdout); + else + writenum((long)info->st_uid,(short)0); + } else +#endif + writenum((long)info->st_uid,(short)0); + tab(24); +#ifdef FEATURE_USERNAME + if (!(opts & DISP_NUMERIC)) { + struct group *gr = getgrgid(info->st_gid); + if (gr) + fputs(gr->gr_name, stdout); + else + writenum((long)info->st_gid,(short)0); + } else +#endif + writenum((long)info->st_gid,(short)0); + tab(33); + if (S_ISBLK(mode) || S_ISCHR(mode)) { + writenum((long)MAJOR(info->st_rdev),(short)3); + fputs(", ", stdout); + writenum((long)MINOR(info->st_rdev),(short)3); + } + else + writenum((long)info->st_size,(short)8); + fputs(" ", stdout); +#ifdef FEATURE_TIMESTAMPS + { + time_t cal; + char *string; + + switch(time_fmt) { + case TIME_CHANGE: + cal=info->st_ctime; break; + case TIME_ACCESS: + cal=info->st_atime; break; + default: + cal=info->st_mtime; break; + } + string=ctime(&cal); + if (opts & DISP_FULLTIME) + wr(string,24); + else { + time_t age = time(NULL) - cal; + wr(string+4,7); /* mmm_dd_ */ + if(age < 3600L*24*365/2 && age > -15*60) + /* hh:mm if less than 6 months old */ + wr(string+11,5); + else + /* _yyyy otherwise */ + wr(string+19,5); + } + wr(" ", 1); + } +#else + fputs("--- -- ----- ", stdout); +#endif + wr(name, len); + if (S_ISLNK(mode)) { + wr(" -> ", 4); + len = readlink(name, scratch, sizeof scratch); + if (len > 0) fwrite(scratch, 1, len, stdout); +#ifdef FEATURE_FILETYPECHAR + /* show type of destination */ + if (opts & DISP_FTYPE) { + if (!stat(name, info)) { + append = append_char(info->st_mode); + if (append) + fputc(append, stdout); + } + } +#endif + } +#ifdef FEATURE_FILETYPECHAR + else if (append) + wr(&append, 1); +#endif + } else { + static short nexttab = 0; + + /* sort out column alignment */ + if (column == 0) + ; /* nothing to do */ + else if (display_fmt == FMT_SINGLE) + newline(); + else { + if (nexttab + column_width > terminal_width +#ifndef FEATURE_AUTOWIDTH + || nexttab + len >= terminal_width +#endif + ) + newline(); + else + tab(nexttab); + } + /* work out where next column starts */ +#ifdef FEATURE_AUTOWIDTH + /* we know the calculated width is big enough */ + nexttab = column + column_width + COLUMN_GAP; +#else + /* might cover more than one fixed-width column */ + nexttab = column; + do + nexttab += column_width + COLUMN_GAP; + while (nexttab < (column + len + COLUMN_GAP)); +#endif + /* now write the data */ + wr(name, len); + column = column + len; +#ifdef FEATURE_FILETYPECHAR + if (append) + wr(&append, 1), column++; +#endif + } +} + +/** + ** + ** List the given file or directory, expanding a directory + ** to show its contents if required + ** + **/ + +static int list_item(const char *name) +{ + struct stat info; + DIR *dir; + struct dirent *entry; + char fullname[MAXNAMLEN+1], *fnend; + + if (lstat(name, &info)) + goto listerr; + + if (!S_ISDIR(info.st_mode) || + (opts & DIR_NOLIST)) { + list_single(name, &info); + return 0; + } + + /* Otherwise, it's a directory we want to list the contents of */ + + if (opts & DISP_DIRNAME) { /* identify the directory */ + if (column) + wr("\n\n", 2), column = 0; + wr(name, strlen(name)); + wr(":\n", 2); + } + + dir = opendir(name); + if (!dir) goto listerr; +#ifdef FEATURE_AUTOWIDTH + column_width = 0; + while ((entry = readdir(dir)) != NULL) { + short w = strlen(entry->d_name); + if (column_width < w) + column_width = w; + } +#ifdef HAS_REWINDDIR + rewinddir(dir); +#else + closedir(dir); + dir = opendir(name); + if (!dir) goto listerr; +#endif +#endif + + /* List the contents */ + + strcpy(fullname,name); /* *** ignore '.' by itself */ + fnend=fullname+strlen(fullname); + if (fnend[-1] != '/') + *fnend++ = '/'; + + while ((entry = readdir(dir)) != NULL) { + const char *en=entry->d_name; + if (en[0] == '.') { + if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ + if (!(opts & DISP_DOT)) + continue; + } + else if (!(opts & DISP_HIDDEN)) + continue; + } + /* FIXME: avoid stat if not required */ + strcpy(fnend, entry->d_name); + if (lstat(fullname, &info)) + goto direrr; /* (shouldn't fail) */ + list_single(entry->d_name, &info); + } + closedir(dir); + return 0; + +direrr: + closedir(dir); +listerr: + newline(); + name_and_error(name); + return 1; +} + +const char ls_usage[] = "Usage: ls [-1a" +#ifdef FEATURE_TIMESTAMPS + "c" +#endif + "d" +#ifdef FEATURE_TIMESTAMPS + "e" +#endif + "ln" +#ifdef FEATURE_FILETYPECHAR + "p" +#endif +#ifdef FEATURE_TIMESTAMPS + "u" +#endif + "xAC" +#ifdef FEATURE_FILETYPECHAR + "F" +#endif +#ifdef FEATURE_RECURSIVE + "R" +#endif + "] [filenames...]\n"; + +extern int +ls_main(struct FileInfo * not_used, int argc, char * * argv) +{ + int argi=1, i; + + /* process options */ + while (argi < argc && argv[argi][0] == '-') { + const char *p = &argv[argi][1]; + + if (!*p) goto print_usage_message; /* "-" by itself not allowed */ + if (*p == '-') { + if (!p[1]) { /* "--" forces end of options */ + argi++; + break; + } + /* it's a long option name - we don't support them */ + goto print_usage_message; + } + + while (*p) + switch (*p++) { + case 'l': display_fmt = FMT_LONG; break; + case '1': display_fmt = FMT_SINGLE; break; + case 'x': display_fmt = FMT_ROWS; break; + case 'C': display_fmt = FMT_COLUMNS; break; +#ifdef FEATURE_FILETYPECHAR + case 'p': opts |= DISP_FTYPE; break; + case 'F': opts |= DISP_FTYPE|DISP_EXEC; break; +#endif + case 'A': opts |= DISP_HIDDEN; break; + case 'a': opts |= DISP_HIDDEN|DISP_DOT; break; + case 'n': opts |= DISP_NUMERIC; break; + case 'd': opts |= DIR_NOLIST; break; +#ifdef FEATURE_RECURSIVE + case 'R': opts |= DIR_RECURSE; break; +#endif +#ifdef FEATURE_TIMESTAMPS + case 'u': time_fmt = TIME_ACCESS; break; + case 'c': time_fmt = TIME_CHANGE; break; + case 'e': opts |= DISP_FULLTIME; break; +#endif + default: goto print_usage_message; + } + + argi++; + } + + /* choose a display format */ + if (display_fmt == FMT_AUTO) + display_fmt = isatty(STDOUT_FILENO) ? FMT_COLUMNS : FMT_SINGLE; + if (argi < argc - 1) + opts |= DISP_DIRNAME; /* 2 or more items? label directories */ +#ifdef FEATURE_AUTOWIDTH + /* could add a -w option and/or TIOCGWINSZ call */ + if (terminal_width < 1) terminal_width = TERMINAL_WIDTH; + + for (i = argi; i < argc; i++) { + int len = strlen(argv[i]); + if (column_width < len) + column_width = len; + } +#endif + + /* process files specified, or current directory if none */ + i=0; + if (argi == argc) + i = list_item("."); + while (argi < argc) + i |= list_item(argv[argi++]); + newline(); + return i; + +print_usage_message: + usage(ls_usage); + return 1; +} diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c new file mode 100644 index 000000000..8f1fa04db --- /dev/null +++ b/coreutils/mkdir.c @@ -0,0 +1,58 @@ +#include "internal.h" +#include +#include + +const char mkdir_usage[] = "mkdir [-m mode] directory [directory ...]\n" +"\tCreate directories.\n" +"\n" +"\t-m mode:\tSpecifiy the mode for the new directory\n" +"\t\tunder the argument directory."; + +/*make directories skipping the last part of the path. Used here and by untar*/ +int mkdir_until(const char *fpath, const struct FileInfo * fi) +{ + char path[PATH_MAX]; + char * s = path; + + strcpy(path, fpath); + if ( s[0] == '\0' && s[1] == '\0' ) { + usage(mkdir_usage); + return 1; + } + s++; + while ( *s != '\0' ) { + if ( *s == '/' ) { + int status; + + *s = '\0'; + status = mkdir(path, (fi?fi->orWithMode:0700) ); + *s = '/'; + + if ( status != 0 ) { + if ( errno != EEXIST ) { + name_and_error(fpath); + return 1; + } + } + + } + s++; + } + return 0; +} + +int +mkdir_fn(const struct FileInfo * i) +{ + if ( i->makeParentDirectories ) { + if(mkdir_until(i->source, i)) return 1; + } + + if ( mkdir(i->source, i->orWithMode) != 0 && errno != EEXIST ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} + diff --git a/coreutils/mknod.c b/coreutils/mknod.c new file mode 100644 index 000000000..b18394bec --- /dev/null +++ b/coreutils/mknod.c @@ -0,0 +1,52 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char mknod_usage[] = "mknod file b|c|u|p major minor\n" +"\tMake special files.\n" +"\n" +"\tb:\tMake a block (buffered) device.\n" +"\tc or u:\tMake a character (un-buffered) device.\n" +"\tp:\tMake a named pipe. Major and minor are ignored for named pipes.\n"; + +int +mknod_main(struct FileInfo * i, int argc, char * * argv) +{ + mode_t mode = 0; + dev_t dev = 0; + + switch(argv[2][0]) { + case 'c': + case 'u': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'p': + mode = S_IFIFO; + break; + default: + usage(mknod_usage); + return 1; + } + + if ( mode == S_IFCHR || mode == S_IFBLK ) { + dev = (atoi(argv[3]) << 8) | atoi(argv[4]); + if ( argc != 5 ) { + usage(mknod_usage); + return 1; + } + } + + mode |= 0666; + + if ( mknod(argv[1], mode, dev) != 0 ) { + name_and_error(argv[1]); + return 1; + } + return 0; +} diff --git a/coreutils/mv.c b/coreutils/mv.c new file mode 100644 index 000000000..22c4a1207 --- /dev/null +++ b/coreutils/mv.c @@ -0,0 +1,38 @@ +#include "internal.h" +#include +#include + +const char mv_usage[] = "mv source-file destination-file\n" +"\t\tmv source-file [source-file ...] destination-directory\n" +"\n" +"\tMove the source files to the destination.\n" +"\n"; + +extern int +mv_fn(const struct FileInfo * i) +{ + struct stat destination_stat; + char d[1024]; + struct FileInfo n; + + if ( stat(i->destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) + return 0; /* Move file to itself. */ + } + if ( (destination_stat.st_mode & S_IFMT) == S_IFDIR ) { + n = *i; + n.destination = join_paths(d, i->destination, basename(i->source)); + i = &n; + } + if ( rename(i->source, i->destination) == 0 ) + return 0; + else if ( errno == EXDEV && is_a_directory(i->source) ) { + fprintf(stderr + ,"%s: Can't move directory across filesystems.\n" + ,i->source); + return 1; + } + else + return cp_fn(i); +} diff --git a/coreutils/printf.c b/coreutils/printf.c new file mode 100644 index 000000000..e79843c80 --- /dev/null +++ b/coreutils/printf.c @@ -0,0 +1,531 @@ +// I may still need some more cleaning...fix my error checking + +#include "internal.h" +#ifdef BB_PRINTF + +/* printf - format and print data + Copyright (C) 90, 91, 92, 93, 94, 95, 1996 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Usage: printf format [argument...] + + A front end to the printf function that lets it be used from the shell. + + Backslash escapes: + + \" = double quote + \\ = backslash + \a = alert (bell) + \b = backspace + \c = produce no further output + \f = form feed + \n = new line + \r = carriage return + \t = horizontal tab + \v = vertical tab + \0ooo = octal number (ooo is 0 to 3 digits) + \xhhh = hexadecimal number (hhh is 1 to 3 digits) + + Additional directive: + + %b = print an argument string, interpreting backslash escapes + + The `format' argument is re-used as many times as necessary + to convert all of the given arguments. + + David MacKenzie */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef S_IFMT +# define S_IFMT 0170000 +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +#define IN_CTYPE_DOMAIN(c) 1 + +#ifdef isblank +# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c)) +#else +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) +#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c)) +#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) +#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c)) +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0') +#define octtobin(c) ((c) - '0') + +char *xmalloc (); + +static double xstrtod __P ((char *s)); +static int print_esc __P ((char *escstart)); +static int print_formatted __P ((char *format, int argc, char **argv)); +static long xstrtol __P ((char *s)); +static unsigned long xstrtoul __P ((char *s)); +static void print_direc __P ((char *start, size_t length, int field_width, int precision, char *argument)); +static void print_esc_char __P ((int c)); +static void print_esc_string __P ((char *str)); +static void verify __P ((char *s, char *end)); + +/* The value to return to the calling program. */ +static int exit_status; + +const char printf_usage[] = "Usage: printf format [argument...]\n"; + +int +printf_main(struct FileInfo * i, int argc, char * * argv) +{ + char *format; + int args_used; + + exit_status = 0; + + format = argv[1]; + argc -= 2; + argv += 2; + + do + { + args_used = print_formatted (format, argc, argv); + argc -= args_used; + argv += args_used; + } + while (args_used > 0 && argc > 0); + +/* + if (argc > 0) + fprintf(stderr, "excess args ignored"); +*/ + + exit (exit_status); +} + +/* Print the text in FORMAT, using ARGV (with ARGC elements) for + arguments to any `%' directives. + Return the number of elements of ARGV used. */ + +static int +print_formatted (char *format, int argc, char **argv) +{ + int save_argc = argc; /* Preserve original value. */ + char *f; /* Pointer into `format'. */ + char *direc_start; /* Start of % directive. */ + size_t direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*', or -1 if none. */ + int precision; /* Arg to second '*', or -1 if none. */ + + for (f = format; *f; ++f) + { + switch (*f) + { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = -1; + if (*f == '%') + { + putchar ('%'); + break; + } + if (*f == 'b') + { + if (argc > 0) + { + print_esc_string (*argv); + ++argv; + --argc; + } + break; + } + if (strchr ("-+ #", *f)) + { + ++f; + ++direc_length; + } + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + field_width = xstrtoul (*argv); + ++argv; + --argc; + } + else + field_width = 0; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + if (*f == '.') + { + ++f; + ++direc_length; + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + precision = xstrtoul (*argv); + ++argv; + --argc; + } + else + precision = 0; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + } + if (*f == 'l' || *f == 'L' || *f == 'h') + { + ++f; + ++direc_length; + } + /* + if (!strchr ("diouxXfeEgGcs", *f)) + fprintf(stderr, "%%%c: invalid directive", *f); + */ + ++direc_length; + if (argc > 0) + { + print_direc (direc_start, direc_length, field_width, + precision, *argv); + ++argv; + --argc; + } + else + print_direc (direc_start, direc_length, field_width, + precision, ""); + break; + + case '\\': + f += print_esc (f); + break; + + default: + putchar (*f); + } + } + + return save_argc - argc; +} + +/* Print a \ escape sequence starting at ESCSTART. + Return the number of characters in the escape sequence + besides the backslash. */ + +static int +print_esc (char *escstart) +{ + register char *p = escstart + 1; + int esc_value = 0; /* Value of \nnn escape. */ + int esc_length; /* Length of \nnn escape. */ + + /* \0ooo and \xhhh escapes have maximum length of 3 chars. */ + if (*p == 'x') + { + for (esc_length = 0, ++p; + esc_length < 3 && ISXDIGIT (*p); + ++esc_length, ++p) + esc_value = esc_value * 16 + hextobin (*p); +/* if (esc_length == 0) + fprintf(stderr, "missing hex in esc"); +*/ + putchar (esc_value); + } + else if (*p == '0') + { + for (esc_length = 0, ++p; + esc_length < 3 && isodigit (*p); + ++esc_length, ++p) + esc_value = esc_value * 8 + octtobin (*p); + putchar (esc_value); + } + else if (strchr ("\"\\abcfnrtv", *p)) + print_esc_char (*p++); +/* else + fprintf(stderr, "\\%c: invalid esc", *p); +*/ + return p - escstart - 1; +} + +/* Output a single-character \ escape. */ + +static void +print_esc_char (int c) +{ + switch (c) + { + case 'a': /* Alert. */ + putchar (7); + break; + case 'b': /* Backspace. */ + putchar (8); + break; + case 'c': /* Cancel the rest of the output. */ + exit (0); + break; + case 'f': /* Form feed. */ + putchar (12); + break; + case 'n': /* New line. */ + putchar (10); + break; + case 'r': /* Carriage return. */ + putchar (13); + break; + case 't': /* Horizontal tab. */ + putchar (9); + break; + case 'v': /* Vertical tab. */ + putchar (11); + break; + default: + putchar (c); + break; + } +} + +/* Print string STR, evaluating \ escapes. */ + +static void +print_esc_string (char *str) +{ + for (; *str; str++) + if (*str == '\\') + str += print_esc (str); + else + putchar (*str); +} + +static void +print_direc (char *start, size_t length, int field_width, int precision, char *argument) +{ + char *p; /* Null-terminated copy of % directive. */ + + p = xmalloc ((unsigned) (length + 1)); + strncpy (p, start, length); + p[length] = 0; + + switch (p[length - 1]) + { + case 'd': + case 'i': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtol (argument)); + else + printf (p, precision, xstrtol (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtol (argument)); + else + printf (p, field_width, precision, xstrtol (argument)); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtoul (argument)); + else + printf (p, precision, xstrtoul (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtoul (argument)); + else + printf (p, field_width, precision, xstrtoul (argument)); + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtod (argument)); + else + printf (p, precision, xstrtod (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtod (argument)); + else + printf (p, field_width, precision, xstrtod (argument)); + } + break; + + case 'c': + printf (p, *argument); + break; + + case 's': + if (field_width < 0) + { + if (precision < 0) + printf (p, argument); + else + printf (p, precision, argument); + } + else + { + if (precision < 0) + printf (p, field_width, argument); + else + printf (p, field_width, precision, argument); + } + break; + } + + free (p); +} + +static unsigned long +xstrtoul (char *s) +{ + char *end; + unsigned long val; + + errno = 0; + val = strtoul (s, &end, 0); + verify (s, end); + return val; +} + +static long +xstrtol (char *s) +{ + char *end; + long val; + + errno = 0; + val = strtol (s, &end, 0); + verify (s, end); + return val; +} + +static double +xstrtod (char *s) +{ + char *end; + double val; + + errno = 0; + val = strtod (s, &end); + verify (s, end); + return val; +} + +static void +verify (char *s, char *end) +{ + if (errno) + { + fprintf(stderr, "%s", s); + exit_status = 1; + } + else if (*end) + { + /* + if (s == end) + fprintf(stderr, "%s: expected numeric", s); + else + fprintf(stderr, "%s: not completely converted", s); + */ + exit_status = 1; + } +} + +#endif diff --git a/coreutils/pwd.c b/coreutils/pwd.c new file mode 100644 index 000000000..d9ab54e48 --- /dev/null +++ b/coreutils/pwd.c @@ -0,0 +1,18 @@ +#include "internal.h" +#include + +const char pwd_usage[] = "Print the current directory.\n"; + +extern int +pwd_main(struct FileInfo * i, int argc, char * * argv) +{ + char buf[1024]; + + if ( getcwd(buf, sizeof(buf)) == NULL ) { + name_and_error("get working directory"); + return 1; + } + + printf("%s\n", buf); + return 0; +} diff --git a/coreutils/rm.c b/coreutils/rm.c new file mode 100644 index 000000000..dc35b0297 --- /dev/null +++ b/coreutils/rm.c @@ -0,0 +1,30 @@ +#include "internal.h" +#include + +const char rm_usage[] = "rm [-r] file [file ...]\n" +"\n" +"\tDelete files.\n" +"\n" +"\t-r:\tRecursively remove files and directories.\n"; + +extern int +rm_main(struct FileInfo * i, int argc, char * * argv) +{ + i->processDirectoriesAfterTheirContents = 1; + return monadic_main(i, argc, argv); +} + +extern int +rm_fn(const struct FileInfo * i) +{ + if ( i->recursive + && !i->isSymbolicLink + && (i->stat.st_mode & S_IFMT) == S_IFDIR ) + return rmdir_fn(i); + else if ( unlink(i->source) != 0 && errno != ENOENT && !i->force ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c new file mode 100644 index 000000000..069e68546 --- /dev/null +++ b/coreutils/rmdir.c @@ -0,0 +1,17 @@ +#include "internal.h" +#include + +const char rmdir_usage[] = "rmdir directory [directory ...]\n" +"\n" +"\tDelete directories.\n"; + +extern int +rmdir_fn(const struct FileInfo * i) +{ + if ( rmdir(i->source) != 0 && errno != ENOENT && !i->force ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} diff --git a/coreutils/sleep.c b/coreutils/sleep.c new file mode 100644 index 000000000..e48e14b2f --- /dev/null +++ b/coreutils/sleep.c @@ -0,0 +1,15 @@ +#include "internal.h" +#include + +const char sleep_usage[] = "sleep seconds\n" +"\n" +"\tPause program execution for the given number of seconds.\n"; + +extern int +sleep_main(struct FileInfo * i, int argc, char * * argv) +{ + if ( sleep(atoi(argv[1])) != 0 ) + return -1; + else + return 0; +} diff --git a/coreutils/sync.c b/coreutils/sync.c new file mode 100644 index 000000000..6fa5b380b --- /dev/null +++ b/coreutils/sync.c @@ -0,0 +1,11 @@ +#include "internal.h" + +const char sync_usage[] = "sync\n" +"\n" +"\tWrite all buffered filesystem blocks to disk.\n"; + +extern int +sync_main(struct FileInfo * i, int argc, char * * argv) +{ + return sync(); +} diff --git a/coreutils/touch.c b/coreutils/touch.c new file mode 100644 index 000000000..ca4b98108 --- /dev/null +++ b/coreutils/touch.c @@ -0,0 +1,20 @@ +#include "internal.h" +#include +#include +#include + +const char touch_usage[] = "touch [-c] file [file ...]\n" +"\n" +"\tUpdate the last-modified date on the given file[s].\n"; + +extern int +touch_fn(const struct FileInfo * i) +{ + if ( (utime(i->source, 0) != 0) && (i->create != 1) ) { + if ( fopen(i->source, "w") == NULL ) { + name_and_error(i->source); + return 1; + } + } + return 0; +} diff --git a/cp.c b/cp.c new file mode 100644 index 000000000..078a57c56 --- /dev/null +++ b/cp.c @@ -0,0 +1,89 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char cp_usage[] = "cp [-r] source-file destination-file\n" +"\t\tcp [-r] source-file [source-file ...] destination-directory\n" +"\n" +"\tCopy the source files to the destination.\n" +"\n" +"\t-r:\tRecursively copy all files and directories\n" +"\t\tunder the argument directory."; + +extern int +cp_fn(const struct FileInfo * i) +{ + int sourceFd; + int destinationFd; + const char * destination = i->destination; + struct stat destination_stat; + int status; + char buf[8192]; + char d[PATH_MAX]; + + if ( (i->stat.st_mode & S_IFMT) == S_IFDIR ) { + if ( mkdir(destination, i->stat.st_mode & ~S_IFMT) + != 0 && errno != EEXIST ) { + name_and_error(destination); + return 1; + } + return 0; + } + if ( (sourceFd = open(i->source, O_RDONLY)) < 0 ) { + name_and_error(i->source); + return 1; + } + if ( stat(destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) { + fprintf(stderr + ,"copy of %s to %s would copy file to itself.\n" + ,i->source + ,destination); + close(sourceFd); + return 1; + } + } + /* + * If the destination is a directory, create a file within it. + */ + if ( (destination_stat.st_mode & S_IFMT) == S_IFDIR ) { + destination = join_paths( + d + ,i->destination + ,&i->source[i->directoryLength]); + + if ( stat(destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) { + fprintf(stderr + ,"copy of %s to %s would copy file to itself.\n" + ,i->source + ,destination); + close(sourceFd); + return 1; + } + } + } + + destinationFd = creat(destination, i->stat.st_mode & 07777); + + while ( (status = read(sourceFd, buf, sizeof(buf))) > 0 ) { + if ( write(destinationFd, buf, status) != status ) { + name_and_error(destination); + close(sourceFd); + close(destinationFd); + return 1; + } + } + close(sourceFd); + close(destinationFd); + if ( status < 0 ) { + name_and_error(i->source); + return 1; + } + return 0; +} diff --git a/date.c b/date.c new file mode 100644 index 000000000..a52a9a4a8 --- /dev/null +++ b/date.c @@ -0,0 +1,305 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + + +/* This 'date' command supports only 2 time setting formats, + all the GNU strftime stuff (its in libc, lets use it), + setting time using UTC and displaying int, as well as + an RFC 822 complient date output for shell scripting + mail commands */ + +const char date_usage[] = "date [-uR] [+FORMAT|+%f] [ [-s|-d] MMDDhhmm[[CC]YY]\n | [[[[CCYY.]MM.DD-]hh:mm[:ss]]]] ]"; + +static struct option const long_options[] = +{ + {"date", required_argument, NULL, 'd'}, + /* {"rfc-822", no_argument, NULL, 'R'}, + {"set", required_argument, NULL, 's'}, + {"uct", no_argument, NULL, 'u'}, + {"utc", no_argument, NULL, 'u'}, + {"universal", no_argument, NULL, 'u'}, */ + {NULL, 0, NULL, 0} +}; + + + +/* Input parsing code is always bulky - used heavy duty libc stuff as + much as possible, missed out a lot of bounds checking */ + +/* Default input handling to save suprising some people */ + +struct tm * +date_conv_time(struct tm *tm_time, const char *t_string) { + int nr; + + nr = sscanf(t_string, "%2d%2d%2d%2d%d", + &(tm_time->tm_mon), + &(tm_time->tm_mday), + &(tm_time->tm_hour), + &(tm_time->tm_min), + &(tm_time->tm_year)); + + if(nr < 4 || nr > 5) { + fprintf(stderr, "date: invalid date `%s'\n", t_string); + exit(1); + } + + /* correct for century - minor Y2K problem here? */ + if(tm_time->tm_year >= 1900) + tm_time->tm_year -= 1900; + /* adjust date */ + tm_time->tm_mon -= 1; + + return(tm_time); + +} + + +/* The new stuff for LRP */ + +struct tm * +date_conv_ftime(struct tm *tm_time, const char *t_string) { + struct tm itm_time, jtm_time, ktm_time, \ + ltm_time, mtm_time, ntm_time; + + itm_time = *tm_time; + jtm_time = *tm_time; + ktm_time = *tm_time; + ltm_time = *tm_time; + mtm_time = *tm_time; + ntm_time = *tm_time; + + /* Parse input and assign appropriately to tm_time */ + + if(sscanf(t_string, "%d:%d:%d", + &itm_time.tm_hour, + &itm_time.tm_min, + &itm_time.tm_sec) == 3 ) { + + *tm_time = itm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d:%d", + &jtm_time.tm_hour, + &jtm_time.tm_min) == 2) { + + *tm_time = jtm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d-%d:%d:%d", + &ktm_time.tm_mon, + &ktm_time.tm_mday, + &ktm_time.tm_hour, + &ktm_time.tm_min, + &ktm_time.tm_sec) == 5) { + + ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ktm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d-%d:%d", + <m_time.tm_mon, + <m_time.tm_mday, + <m_time.tm_hour, + <m_time.tm_min) == 4) { + + ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ltm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d", + &mtm_time.tm_year, + &mtm_time.tm_mon, + &mtm_time.tm_mday, + &mtm_time.tm_hour, + &mtm_time.tm_min, + &mtm_time.tm_sec) == 6) { + + mtm_time.tm_year -= 1900; /* Adjust years */ + mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = mtm_time; + return(tm_time); + + } else if (sscanf(t_string, "%d.%d.%d-%d:%d", + &ntm_time.tm_year, + &ntm_time.tm_mon, + &ntm_time.tm_mday, + &ntm_time.tm_hour, + &ntm_time.tm_min) == 5) { + ntm_time.tm_year -= 1900; /* Adjust years */ + ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + *tm_time = ntm_time; + return(tm_time); + + } + + fprintf(stderr, "date: invalid date `%s'\n", t_string); + + exit(1); + +} + + +void +date_err(void) { + fprintf(stderr, "date: only one date argument can be given at a time.\n"); + exit(1); +} + +int +date_main(struct FileInfo * i, int argc, char * * argv) +{ + char *date_str = NULL; + char *date_fmt = NULL; + char *t_buff; + int set_time = 0; + int rfc822 = 0; + int utc = 0; + int use_arg = 0; + int n_args; + time_t tm; + struct tm tm_time; + char optc; + + /* Interpret command line args */ + + + while ((optc = getopt_long (argc, argv, "d:Rs:u", long_options, NULL)) + != EOF) { + switch (optc) { + case 0: + break; + + case 'R': + rfc822 = 1; + break; + + case 's': + set_time = 1; + if(date_str != NULL) date_err(); + date_str = optarg; + break; + + case 'u': + utc = 1; + if (putenv ("TZ=UTC0") != 0) { + fprintf(stderr,"date: memory exhausted\n"); + return(1); + } +#if LOCALTIME_CACHE + tzset (); +#endif break; + + case 'd': + use_arg = 1; + if(date_str != NULL) date_err(); + date_str = optarg; + break; + + default: + usage(date_usage); + break; + } + } + + + n_args = argc - optind; + + while (n_args--){ + switch(argv[optind][0]) { + case '+': + /* Date format strings */ + if(date_fmt != NULL) { + fprintf(stderr, "date: only one date format can be given.\n"); + return(1); + } + date_fmt = &argv[optind][1]; + break; + + case '\0': + break; + + default: + /* Anything left over must be a date string to set the time */ + set_time = 1; + if(date_str != NULL) date_err(); + date_str = argv[optind]; + break; + } + optind++; + } + + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + time(&tm); + memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); + /* Zero out fields - take her back to midnight!*/ + if(date_str != NULL) { + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + } + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if(date_str != NULL) { + + if(strchr(date_str, ':') != NULL) { + date_conv_ftime(&tm_time, date_str); + } else { + date_conv_time(&tm_time, date_str); + } + + /* Correct any day of week and day of year etc fields */ + tm = mktime(&tm_time); + if (tm < 0 ) { + fprintf(stderr, "date: invalid date `%s'\n", date_str); + exit(1); + } + + /* if setting time, set it */ + if(set_time) { + if( stime(&tm) < 0) { + fprintf(stderr, "date: can't set date.\n"); + exit(1); + } + } + } + + /* Display output */ + + /* Deal with format string */ + if(date_fmt == NULL) { + date_fmt = (rfc822 + ? (utc + ? "%a, %_d %b %Y %H:%M:%S GMT" + : "%a, %_d %b %Y %H:%M:%S %z") + : "%a %b %e %H:%M:%S %Z %Y"); + + } else if ( *date_fmt == '\0' ) { + /* Imitate what GNU 'date' does with NO format string! */ + printf ("\n"); + return(0); + } + + /* Handle special conversions */ + + if( strncmp( date_fmt, "%f", 2) == 0 ) { + date_fmt = "%Y.%m.%d-%H:%M:%S"; + } + + /* Print OUTPUT (after ALL that!) */ + t_buff = malloc(201); + strftime(t_buff, 200, date_fmt, &tm_time); + printf("%s\n", t_buff); + + return(0); + +} diff --git a/dd.c b/dd.c new file mode 100644 index 000000000..8f1b9d409 --- /dev/null +++ b/dd.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "dd" command, originally taken from sash. + * + * Permission to distribute this code under the GPL has been granted. + * Majorly modified, and bugs fixed for busybox by Erik Andersen + */ + +#include "internal.h" +#ifdef BB_DD + +const char dd_usage[] = +"Copy a file, converting and formatting according to options\n\ +\n\ +usage: [if=name] [of=name] [bs=n] [count=n]\n\ +\tif=FILE\tread from FILE instead of stdin\n\ +\tof=FILE\twrite to FILE instead of stout\n\ +\tbs=n\tread and write N bytes at a time\n\ +\tcount=n\tcopy only n input blocks\n\ +\n\ +BYTES may be suffixed: by k for x1024, b for x512, and w for x2.\n"; + + +#include +#include +#include +#include +#include +#include + + +#define PAR_NONE 0 +#define PAR_IF 1 +#define PAR_OF 2 +#define PAR_BS 3 +#define PAR_COUNT 4 + + +typedef struct +{ + const char * name; + int value; +} PARAM; + + +static const PARAM params[] = +{ + {"if", PAR_IF}, + {"of", PAR_OF}, + {"bs", PAR_BS}, + {"count", PAR_COUNT}, + {NULL, PAR_NONE} +}; + + +static long getNum(const char * cp); + +extern int +dd_main (struct FileInfo *unused, int argc, char **argv) +{ + const char * str; + const PARAM * par; + const char * inFile; + const char * outFile; + char * cp; + int inFd; + int outFd; + int inCc=0; + int outCc; + int blockSize; + long count; + long intotal; + long outTotal; + unsigned char* buf; + unsigned char localBuf[BUF_SIZE]; + + inFile = NULL; + outFile = NULL; + blockSize = 512; + count = 1; + + + while (--argc > 0) + { + str = *++argv; + cp = strchr(str, '='); + + if (cp == NULL) + { + fprintf(stderr, "Bad dd argument\n"); + goto usage; + } + + *cp++ = '\0'; + + for (par = params; par->name; par++) + { + if (strcmp(str, par->name) == 0) + break; + } + + switch (par->value) + { + case PAR_IF: + if (inFile) + { + fprintf(stderr, "Multiple input files illegal\n"); + goto usage; + } + + //fprintf(stderr, "if=%s\n", cp); + inFile = cp; + break; + + case PAR_OF: + if (outFile) + { + fprintf(stderr, "Multiple output files illegal\n"); + goto usage; + } + + //fprintf(stderr, "of=%s\n", cp); + outFile = cp; + break; + + case PAR_BS: + blockSize = getNum(cp); + //fprintf(stderr, "bs=%d\n", blockSize); + + if (blockSize <= 0) + { + fprintf(stderr, "Bad block size value\n"); + goto usage; + } + + break; + + case PAR_COUNT: + count = getNum(cp); + //fprintf(stderr, "count=%ld\n", count); + + if (count < 0) + { + fprintf(stderr, "Bad count value\n"); + goto usage; + } + + break; + + default: + goto usage; + } + } + + buf = localBuf; + + if (blockSize > sizeof(localBuf)) + { + buf = malloc(blockSize); + + if (buf == NULL) + { + fprintf(stderr, "Cannot allocate buffer\n"); + return 1; + } + } + + intotal = 0; + outTotal = 0; + + if (inFile == NULL) + inFd = STDIN; + else + inFd = open(inFile, 0); + + if (inFd < 0) + { + perror(inFile); + + if (buf != localBuf) + free(buf); + + return 1; + } + + if (outFile == NULL) + outFd = STDOUT; + else + outFd = creat(outFile, 0666); + + if (outFd < 0) + { + perror(outFile); + close(inFd); + + if (buf != localBuf) + free(buf); + + return 1; + } + + while ( outTotal < count*blockSize ) + { + inCc = read(inFd, buf, blockSize); + if (inCc < 0) { + perror(inFile); + goto cleanup; + } + //fprintf(stderr, "read in =%d\n", inCc); + intotal += inCc; + cp = buf; + + + while ( intotal > outTotal ) + { + if (outTotal+inCc > count*blockSize) + inCc=count*blockSize-outTotal; + outCc = write(outFd, cp, inCc); + if (outCc < 0) + { + perror(outFile); + goto cleanup; + } + //fprintf(stderr, "wrote out =%d\n", outCc); + + inCc -= outCc; + cp += outCc; + outTotal += outCc; + //fprintf(stderr, "outTotal=%ld\n", outTotal); + } + } + + if (inCc < 0) + perror(inFile); + +cleanup: + close(inFd); + + if (close(outFd) < 0) + perror(outFile); + + if (buf != localBuf) + free(buf); + + printf("%ld+%d records in\n", intotal / blockSize, + (intotal % blockSize) != 0); + + printf("%ld+%d records out\n", outTotal / blockSize, + (outTotal % blockSize) != 0); + return 0; +usage: + + fprintf(stderr, "%s", dd_usage); + return 1; +} + + +/* + * Read a number with a possible multiplier. + * Returns -1 if the number format is illegal. + */ +static long +getNum(const char * cp) +{ + long value; + + if (!isDecimal(*cp)) + return -1; + + value = 0; + + while (isDecimal(*cp)) + value = value * 10 + *cp++ - '0'; + + switch (*cp++) + { + case 'k': + value *= 1024; + break; + + case 'b': + value *= 512; + break; + + case 'w': + value *= 2; + break; + + case '\0': + return value; + + default: + return -1; + } + + if (*cp) + return -1; + + return value; +} + +#endif +/* END CODE */ + diff --git a/descend.c b/descend.c new file mode 100644 index 000000000..9ada7b4fc --- /dev/null +++ b/descend.c @@ -0,0 +1,124 @@ +#include "internal.h" +#include +#include +#include +#include + + +static int +noDots(const struct dirent * e) +{ + if ( e->d_name[0] == '.' + && (e->d_name[1] == '\0' + || (e->d_name[1] == '.' && e->d_name[2] == '\0')) ) + return 0; + else + return 1; +} + +extern int +descend( + struct FileInfo *oldInfo +,int (*function)(const struct FileInfo * i)) +{ + char pathname[1024]; + struct dirent * * names; + struct dirent * * n; + int length; + char * filename; + int status = 0; + int count; + + if ( *oldInfo->source == '\0' ) { + errno = EINVAL; + return -1; + } + + if ( oldInfo->stat.st_dev == 0 + && oldInfo->stat.st_ino == 0 + && oldInfo->stat.st_mode == 0 ) { + if ( lstat(oldInfo->source, &oldInfo->stat) != 0 ) + return -1; + oldInfo->isSymbolicLink = ((oldInfo->stat.st_mode & S_IFMT) == S_IFLNK); + + if ( oldInfo->isSymbolicLink ) + if ( stat(oldInfo->source, &oldInfo->stat) != 0 ) + memset((void *)&oldInfo->stat, 0, sizeof(oldInfo->stat)); + } + + if ( !oldInfo->processDirectoriesAfterTheirContents ) { + if ( function ) + status = (*function)(oldInfo); + if ( status == 0 ) + status = post_process(oldInfo); + } + + if ( (count = scandir(oldInfo->source, &names, noDots, alphasort)) < 0 ) + return -1; + + length = strlen(oldInfo->source); + if ( oldInfo->source[length-1] == '/' ) + length--; + + memcpy(pathname, oldInfo->source, length+1); + pathname[length] = '/'; + filename = &pathname[length+1]; + + n = names; + while ( count-- > 0 ) { + struct FileInfo i = *oldInfo; + + strcpy(filename, (*n)->d_name); + free(*n++); + + if ( lstat(pathname, &i.stat) != 0 && errno != ENOENT ) { + fprintf(stderr, "Can't stat %s: %s\n", pathname, strerror(errno)); + return -1; + } + i.isSymbolicLink = ((i.stat.st_mode & S_IFMT) == S_IFLNK); + + if ( i.isSymbolicLink ) + if ( stat(pathname, &i.stat) != 0 ) + memset((void *)&i.stat, 0, sizeof(i.stat)); + + i.source = pathname; + + if ( i.dyadic ) { + char d[1024]; + + i.destination = join_paths(d, i.destination, &i.source[i.directoryLength]); + } + else + i.destination = i.source; + + if ( !i.isSymbolicLink && (i.stat.st_mode & S_IFMT) == S_IFDIR ) + status = descend(&i, function); + else { + if ( function ) + status = (*function)(&i); + if ( status == 0 ) + status = post_process(&i); + } + + if ( !i.processDirectoriesAfterTheirContents + && status == 0 + && (i.stat.st_mode & S_IFMT) == S_IFDIR ) + descend(&i, function); + + if ( status != 0 && !i.force ) { + while ( count-- > 0 ) + free(*n++); + break; + } + } + free(names); + + if ( oldInfo->processDirectoriesAfterTheirContents ) { + if ( function ) + status = (*function)(oldInfo); + if ( status == 0 ) + status = post_process(oldInfo); + } + + return status; +} diff --git a/df.c b/df.c new file mode 100644 index 000000000..a0692afc5 --- /dev/null +++ b/df.c @@ -0,0 +1,103 @@ +#include "internal.h" +#include +#include +#include +#include + +const char df_usage[] = "df [filesystem ...]\n" +"\n" +"\tPrint the filesystem space used and space available.\n"; + + +static int +df(const char * device, const char * mountPoint) +{ + struct statfs s; + long blocks_used; + long blocks_percent_used; + + if ( statfs(mountPoint, &s) != 0 ) { + name_and_error(mountPoint); + return 1; + } + + if ( s.f_blocks > 0 ) { + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = (long) + (blocks_used * 100.0 / (blocks_used + s.f_bavail) + 0.5); + +/* + printf( + "%-20s %7ld %7ld %7ld %5ld%% %s\n" + ,device + ,s.f_blocks + ,s.f_blocks - s.f_bfree + ,s.f_bavail + ,blocks_percent_used + ,mountPoint); +*/ + + printf( + "%-20s %7.0f %7.0f %7.0f %5ld%% %s\n" + ,device + ,s.f_blocks * (s.f_bsize / 1024.0) + ,(s.f_blocks - s.f_bfree) * (s.f_bsize / 1024.0) + ,s.f_bavail * (s.f_bsize / 1024.0) + ,blocks_percent_used + ,mountPoint); + + } + + return 0; +} + +extern int +df_main(struct FileInfo * i, int argc, char * * argv) +{ + static const char header[] = + "Filesystem 1024-blocks Used Available Capacity Mounted on\n"; + printf(header); + + if ( argc > 1 ) { + struct mntent * mountEntry; + int status; + + while ( argc > 1 ) { + if ( (mountEntry = findMountPoint(argv[1], "/etc/mtab")) == 0 + && (mountEntry = findMountPoint(argv[1], "/proc/mounts")) == 0 ) + { + fprintf(stderr, "%s: can't find mount point.\n" + ,argv[1]); + return 1; + } + status = df(mountEntry->mnt_fsname, mountEntry->mnt_dir); + if ( status != 0 ) + return status; + argc--; + argv++; + } + return 0; + } + else { + FILE * mountTable; + struct mntent * mountEntry; + + if ( (mountTable = setmntent("/etc/mtab", "r")) == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 + ) { + name_and_error("/etc/mtab"); + return 1; + } + + while ( (mountEntry = getmntent(mountTable)) != 0 ) { + int status = df( + mountEntry->mnt_fsname + ,mountEntry->mnt_dir); + if ( status != 0 ) + return status; + } + endmntent(mountTable); + } + + return 0; +} diff --git a/dmesg.c b/dmesg.c new file mode 100644 index 000000000..a63fa3d39 --- /dev/null +++ b/dmesg.c @@ -0,0 +1,95 @@ +#include "internal.h" +#include +#include +#include + +/* dmesg.c -- Print out the contents of the kernel ring buffer + * Created: Sat Oct 9 16:19:47 1993 + * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu + * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu) + * This program comes with ABSOLUTELY NO WARRANTY. + * Modifications by Rick Sladkey (jrs@world.std.com) + * from util-linux; adapted for busybox + */ + +#include +#include +#include + +#define __NR_klog __NR_syslog + +#if defined(__GLIBC__) +#include +#define klog klogctl +#else +static inline _syscall3(int,klog,int,type,char *,b,int,len) +#endif /* __GLIBC__ */ + +const char dmesg_usage[] = "dmesg"; + +int +dmesg_main(struct FileInfo * info, int argc, char * * argv) +{ + + char buf[4096]; + int i; + int n; + int c; + int level = 0; + int lastc; + int cmd = 3; + + while ((c = getopt( argc, argv, "cn:" )) != EOF) { + switch (c) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + level = atoi(optarg); + break; + case '?': + default: + usage(dmesg_usage); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) { + usage(dmesg_usage); + exit(1); + } + + if (cmd == 8) { + n = klog( cmd, NULL, level ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + exit( 0 ); + } + + n = klog( cmd, buf, sizeof( buf ) ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + + lastc = '\n'; + for (i = 0; i < n; i++) { + if ((i == 0 || buf[i - 1] == '\n') && buf[i] == '<') { + i++; + while (buf[i] >= '0' && buf[i] <= '9') + i++; + if (buf[i] == '>') + i++; + } + lastc = buf[i]; + putchar( lastc ); + } + if (lastc != '\n') + putchar( '\n' ); + return 0; +} diff --git a/dutmp.c b/dutmp.c new file mode 100644 index 000000000..e92b6700f --- /dev/null +++ b/dutmp.c @@ -0,0 +1,47 @@ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * dutmp + * Takes utmp formated file on stdin and dumps it's contents + * out in colon delimited fields. Easy to 'cut' for shell based + * versions of 'who', 'last', etc. IP Addr is output in hex, + * little endian on x86. + * + * made against libc6 + */ + +#include "internal.h" +#include +#include + +const char dutmp_usage[] = "dutmp\n" +"\n" +"\tDump file or stdin utmp file format to stdout, pipe delimited.\n" +"\tdutmp /var/run/utmp\n"; + +extern int +dutmp_fn(const struct FileInfo * i) +{ + +FILE * f = stdin; +struct utmp * ut = (struct utmp *) malloc(sizeof(struct utmp) ); + + if ( i ) + if (! (f = fopen(i->source, "r"))) { + name_and_error(i->source); + return 1; + } + + while (fread (ut, 1, sizeof(struct utmp), f)) { + //printf("%d:%d:%s:%s:%s:%s:%d:%d:%ld:%ld:%ld:%x\n", + printf("%d|%d|%s|%s|%s|%s|%d|%d|%ld|%ld|%ld|%x\n", + ut->ut_type, ut->ut_pid, ut->ut_line, + ut->ut_id, ut->ut_user, ut->ut_host, + ut->ut_exit.e_termination, ut->ut_exit.e_exit, + ut->ut_session, + ut->ut_tv.tv_sec, ut->ut_tv.tv_usec, + ut->ut_addr); + } + +return 0; +} diff --git a/dyadic.c b/dyadic.c new file mode 100644 index 000000000..8136bb573 --- /dev/null +++ b/dyadic.c @@ -0,0 +1,28 @@ +#include "internal.h" +#include + +extern int +dyadic_main( + struct FileInfo * i +,int argc +,char * * argv) +{ + int flags; + + i->dyadic = 1; + i->destination = argv[argc - 1]; + + for ( flags = 0; flags < (argc - 1) && argv[flags + 1][0] == '-' ; flags++ ) + ; + if ( argc - flags < 3 ) { + usage(i->applet->usage); + return 1; + } + else if ( argc - flags > 3 ) { + if ( !is_a_directory(i->destination) ) { + fprintf(stderr, "%s: not a directory.\n", i->destination); + return 1; + } + } + return monadic_main(i, argc - 1, argv); +} diff --git a/examples/busybox.spec b/examples/busybox.spec new file mode 100644 index 000000000..46bd7f484 --- /dev/null +++ b/examples/busybox.spec @@ -0,0 +1,43 @@ +Name: busybox +Version: 0.29alpha +Release: 1 +Group: System/Utilities +Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary. +Copyright: GPL +Packager : Erik Andersen +Conflicts: fileutils grep shellutils +Buildroot: /tmp/%{Name}-%{Version} +Source: busybox-0.29a1.tar.gz + +%Description +BusyBox is a suite of "tiny" Unix utilities in a multi-call binary. It +provides a pretty complete environment that fits on a floppy or in a +ROM. Just add "ash" (Keith Almquists tiny Bourne shell clone) and "ae", +and a kernel and you have a full system. This is used on the Debian +install disk and in an internet router, and it makes a good environment +for a "rescue" disk or any small or embedded system. + +%Prep +%setup -q -n busybox + +%Build +make + +%Install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/bin +h=`cat busybox.links` + +for i in $h ; do + mkdir -p $RPM_BUILD_ROOT/`echo $i | sed -e 's/\(^.*\/\)\(.*\)/\1/g' ` + (cd $RPM_BUILD_ROOT/bin ; ln -s ln `echo $i | sed -e 's/\(^.*\/\)\(.*\)/\2/g' ` ); +done +rm -f $RPM_BUILD_ROOT/bin/ln +install -m 755 busybox $RPM_BUILD_ROOT/bin/ln + +%Clean +rm -rf $RPM_BUILD_ROOT + +%Files +%defattr(-,root,root) +/ diff --git a/fdflush.c b/fdflush.c new file mode 100644 index 000000000..a15e9b3f7 --- /dev/null +++ b/fdflush.c @@ -0,0 +1,36 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char fdflush_usage[] = "fdflush device"; + +int +fdflush(const char *filename) +{ + int status; + int fd = open(filename, 0); + + if ( fd < 0 ) { + name_and_error(filename); + return 1; + } + + status = ioctl(fd, FDFLUSH, 0); + close(fd); + + if ( status != 0 ) { + name_and_error(filename); + return 1; + } + return 0; +} + + +int +fdflush_fn(const struct FileInfo * i) +{ + return fdflush(i->source); +} diff --git a/find.c b/find.c new file mode 100644 index 000000000..b3ac1ff5a --- /dev/null +++ b/find.c @@ -0,0 +1,23 @@ +#include "internal.h" +#include +#include + +const char find_usage[] = "find dir [pattern]\n" +"\n" +"\tFind files.\n"; + +extern int +find_main(struct FileInfo * i, int argc, char * * argv) +{ + i->recursive=1; + i->processDirectoriesAfterTheirContents=1; + return monadic_main(i, argc, argv); +} + +extern int +find_fn(const struct FileInfo * i) +{ + printf("%s\n",i->source); + + return(0); +} diff --git a/findmount.c b/findmount.c new file mode 100644 index 000000000..26e28fcd3 --- /dev/null +++ b/findmount.c @@ -0,0 +1,46 @@ +#include "internal.h" +#include +#include +#include + +/* + * Given a block device, find the mount table entry if that block device + * is mounted. + * + * Given any other file (or directory), find the mount table entry for its + * filesystem. + */ +extern struct mntent * +findMountPoint(const char * name, const char * table) +{ + struct stat s; + dev_t mountDevice; + FILE * mountTable; + struct mntent * mountEntry; + + if ( stat(name, &s) != 0 ) + return 0; + + if ( (s.st_mode & S_IFMT) == S_IFBLK ) + mountDevice = s.st_rdev; + else + mountDevice = s.st_dev; + + + if ( (mountTable = setmntent(table, "r")) == 0 ) + return 0; + + while ( (mountEntry = getmntent(mountTable)) != 0 ) { + if ( strcmp(name, mountEntry->mnt_dir) == 0 + || strcmp(name, mountEntry->mnt_fsname) == 0 ) /* String match. */ + break; + if ( stat(mountEntry->mnt_fsname, &s) == 0 + && s.st_rdev == mountDevice ) /* Match the device. */ + break; + if ( stat(mountEntry->mnt_dir, &s) == 0 + && s.st_dev == mountDevice ) /* Match the directory's mount point. */ + break; + } + endmntent(mountTable); + return mountEntry; +} diff --git a/findutils/find.c b/findutils/find.c new file mode 100644 index 000000000..b3ac1ff5a --- /dev/null +++ b/findutils/find.c @@ -0,0 +1,23 @@ +#include "internal.h" +#include +#include + +const char find_usage[] = "find dir [pattern]\n" +"\n" +"\tFind files.\n"; + +extern int +find_main(struct FileInfo * i, int argc, char * * argv) +{ + i->recursive=1; + i->processDirectoriesAfterTheirContents=1; + return monadic_main(i, argc, argv); +} + +extern int +find_fn(const struct FileInfo * i) +{ + printf("%s\n",i->source); + + return(0); +} diff --git a/findutils/grep.c b/findutils/grep.c new file mode 100644 index 000000000..3779e5510 --- /dev/null +++ b/findutils/grep.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "grep" command, taken from sash. + * This provides basic file searching. + * + * Permission to distribute this code under the GPL has been granted. + * Modified for busybox by Erik Andersen + */ + +#include "internal.h" +#ifdef BB_GREP + +#include +#include +#include +#include +#include +#include +#include + + +const char grep_usage[] = +"Search the input file(s) for lines matching the given pattern.\n" +"\tI search stdin if no files are given.\n" +"\tI can't grok full regular expressions.\n" +"usage: grep [in] PATTERN [FILES]...\n" +"\ti=ignore case, n=list line numbers\n"; + + + +static BOOL search + (const char * string, const char * word, BOOL ignoreCase); + + +extern int +grep_main(struct FileInfo * unused, int argc, char ** argv) +{ + FILE * fp; + const char * word; + const char * name; + const char * cp; + BOOL tellName; + BOOL ignoreCase; + BOOL tellLine; + long line; + char buf[BUF_SIZE]; + + ignoreCase = FALSE; + tellLine = FALSE; + + argc--; + argv++; + if (argc < 1) + { + fprintf(stderr, "%s", grep_usage); + return 1; + } + + if (**argv == '-') + { + argc--; + cp = *argv++; + + while (*++cp) switch (*cp) + { + case 'i': + ignoreCase = TRUE; + break; + + case 'n': + tellLine = TRUE; + break; + + default: + fprintf(stderr, "Unknown option\n"); + return 1; + } + } + + word = *argv++; + argc--; + + tellName = (argc > 1); + + while (argc-- > 0) + { + name = *argv++; + + fp = fopen(name, "r"); + + if (fp == NULL) + { + perror(name); + + continue; + } + + line = 0; + + while (fgets(buf, sizeof(buf), fp)) + { + line++; + + cp = &buf[strlen(buf) - 1]; + + if (*cp != '\n') + fprintf(stderr, "%s: Line too long\n", name); + + if (search(buf, word, ignoreCase)) + { + if (tellName) + printf("%s: ", name); + + if (tellLine) + printf("%ld: ", line); + + fputs(buf, stdout); + } + } + + if (ferror(fp)) + perror(name); + + fclose(fp); + } + return 0; +} + + +/* + * See if the specified word is found in the specified string. + */ +static BOOL +search(const char * string, const char * word, BOOL ignoreCase) +{ + const char * cp1; + const char * cp2; + int len; + int lowFirst; + int ch1; + int ch2; + + len = strlen(word); + + if (!ignoreCase) + { + while (TRUE) + { + string = strchr(string, word[0]); + + if (string == NULL) + return FALSE; + + if (memcmp(string, word, len) == 0) + return TRUE; + + string++; + } + } + + /* + * Here if we need to check case independence. + * Do the search by lower casing both strings. + */ + lowFirst = *word; + + if (isupper(lowFirst)) + lowFirst = tolower(lowFirst); + + while (TRUE) + { + while (*string && (*string != lowFirst) && + (!isupper(*string) || (tolower(*string) != lowFirst))) + { + string++; + } + + if (*string == '\0') + return FALSE; + + cp1 = string; + cp2 = word; + + do + { + if (*cp2 == '\0') + return TRUE; + + ch1 = *cp1++; + + if (isupper(ch1)) + ch1 = tolower(ch1); + + ch2 = *cp2++; + + if (isupper(ch2)) + ch2 = tolower(ch2); + + } + while (ch1 == ch2); + + string++; + } +} + +#endif +/* END CODE */ diff --git a/grep.c b/grep.c new file mode 100644 index 000000000..3779e5510 --- /dev/null +++ b/grep.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "grep" command, taken from sash. + * This provides basic file searching. + * + * Permission to distribute this code under the GPL has been granted. + * Modified for busybox by Erik Andersen + */ + +#include "internal.h" +#ifdef BB_GREP + +#include +#include +#include +#include +#include +#include +#include + + +const char grep_usage[] = +"Search the input file(s) for lines matching the given pattern.\n" +"\tI search stdin if no files are given.\n" +"\tI can't grok full regular expressions.\n" +"usage: grep [in] PATTERN [FILES]...\n" +"\ti=ignore case, n=list line numbers\n"; + + + +static BOOL search + (const char * string, const char * word, BOOL ignoreCase); + + +extern int +grep_main(struct FileInfo * unused, int argc, char ** argv) +{ + FILE * fp; + const char * word; + const char * name; + const char * cp; + BOOL tellName; + BOOL ignoreCase; + BOOL tellLine; + long line; + char buf[BUF_SIZE]; + + ignoreCase = FALSE; + tellLine = FALSE; + + argc--; + argv++; + if (argc < 1) + { + fprintf(stderr, "%s", grep_usage); + return 1; + } + + if (**argv == '-') + { + argc--; + cp = *argv++; + + while (*++cp) switch (*cp) + { + case 'i': + ignoreCase = TRUE; + break; + + case 'n': + tellLine = TRUE; + break; + + default: + fprintf(stderr, "Unknown option\n"); + return 1; + } + } + + word = *argv++; + argc--; + + tellName = (argc > 1); + + while (argc-- > 0) + { + name = *argv++; + + fp = fopen(name, "r"); + + if (fp == NULL) + { + perror(name); + + continue; + } + + line = 0; + + while (fgets(buf, sizeof(buf), fp)) + { + line++; + + cp = &buf[strlen(buf) - 1]; + + if (*cp != '\n') + fprintf(stderr, "%s: Line too long\n", name); + + if (search(buf, word, ignoreCase)) + { + if (tellName) + printf("%s: ", name); + + if (tellLine) + printf("%ld: ", line); + + fputs(buf, stdout); + } + } + + if (ferror(fp)) + perror(name); + + fclose(fp); + } + return 0; +} + + +/* + * See if the specified word is found in the specified string. + */ +static BOOL +search(const char * string, const char * word, BOOL ignoreCase) +{ + const char * cp1; + const char * cp2; + int len; + int lowFirst; + int ch1; + int ch2; + + len = strlen(word); + + if (!ignoreCase) + { + while (TRUE) + { + string = strchr(string, word[0]); + + if (string == NULL) + return FALSE; + + if (memcmp(string, word, len) == 0) + return TRUE; + + string++; + } + } + + /* + * Here if we need to check case independence. + * Do the search by lower casing both strings. + */ + lowFirst = *word; + + if (isupper(lowFirst)) + lowFirst = tolower(lowFirst); + + while (TRUE) + { + while (*string && (*string != lowFirst) && + (!isupper(*string) || (tolower(*string) != lowFirst))) + { + string++; + } + + if (*string == '\0') + return FALSE; + + cp1 = string; + cp2 = word; + + do + { + if (*cp2 == '\0') + return TRUE; + + ch1 = *cp1++; + + if (isupper(ch1)) + ch1 = tolower(ch1); + + ch2 = *cp2++; + + if (isupper(ch2)) + ch2 = tolower(ch2); + + } + while (ch1 == ch2); + + string++; + } +} + +#endif +/* END CODE */ diff --git a/gzip.c b/gzip.c new file mode 100644 index 000000000..6fd2e3971 --- /dev/null +++ b/gzip.c @@ -0,0 +1,3231 @@ +/* gzip.c -- this is a stripped down version of gzip I put into busybox, it does + * only standard in to standard out with -9 compression. It also requires the + * zcat module for some important functions. + * + * Charles P. Wright + */ +#include "internal.h" +#ifdef BB_GZIP + +#ifndef BB_ZCAT +error: you need zcat to have gzip support! +#endif + +const char gzip_usage[] = "gzip\nignores all command line arguments\ncompress stdin to stdout with -9 compression\n"; + +/* gzip.h -- common declarations for all gzip modules + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if defined(__STDC__) || defined(PROTO) +# define OF(args) args +#else +# define OF(args) () +#endif + +#ifdef __STDC__ + typedef void *voidp; +#else + typedef char *voidp; +#endif + +/* I don't like nested includes, but the string and io functions are used + * too often + */ +#include +#if !defined(NO_STRING_H) || defined(STDC_HEADERS) +# include +# if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__) +# include +# endif +# define memzero(s, n) memset ((voidp)(s), 0, (n)) +#else +# include +# define strchr index +# define strrchr rindex +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) +# define memzero(s, n) bzero((s), (n)) +#endif + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#define local static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +/* Return codes from gzip */ +#define OK 0 +#define ERROR 1 +#define WARNING 2 + +/* Compression methods (see algorithm.doc) */ +#define STORED 0 +#define COMPRESSED 1 +#define PACKED 2 +#define LZHED 3 +/* methods 4 to 7 reserved */ +#define DEFLATED 8 +#define MAX_METHODS 9 +extern int method; /* compression method */ + +/* To save memory for 16 bit systems, some arrays are overlaid between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * inflate: window inbuf + * unpack: window inbuf prefix_len + * unlzh: left+right window c_table inbuf c_len + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +#ifdef DYN_ALLOC +# define EXTERN(type, array) extern type * near array +# define DECLARE(type, array, size) type * near array +# define ALLOC(type, array, size) { \ + array = (type*)fcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error("insufficient memory"); \ + } +# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} +#else +# define EXTERN(type, array) extern type array[] +# define DECLARE(type, array, size) type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +EXTERN(uch, inbuf); /* input buffer */ +EXTERN(uch, outbuf); /* output buffer */ +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ +#define tab_suffix window +#ifndef MAXSEG_64K +# define tab_prefix prev /* hash link (see deflate.c) */ +# define head (prev+WSIZE) /* hash head (see deflate.c) */ + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ +#else +# define tab_prefix0 prev +# define head tab_prefix1 + EXTERN(ush, tab_prefix0); /* prefix for even codes */ + EXTERN(ush, tab_prefix1); /* prefix for odd codes */ +#endif + +extern unsigned insize; /* valid bytes in inbuf */ +extern unsigned inptr; /* index of next byte to be processed in inbuf */ +extern unsigned outcnt; /* bytes in output buffer */ + +extern long bytes_in; /* number of input bytes */ +extern long bytes_out; /* number of output bytes */ +extern long header_bytes;/* number of bytes in gzip header */ + +#define isize bytes_in +/* for compatibility with old zip sources (to be cleaned) */ + +extern int ifd; /* input file descriptor */ +extern int ofd; /* output file descriptor */ +extern char ifname[]; /* input file name or "stdin" */ +extern char ofname[]; /* output file name or "stdout" */ +extern char *progname; /* program name */ + +extern long time_stamp; /* original time stamp (modification time) */ +extern long ifile_size; /* input file size, -1 for devices (debug only) */ + +typedef int file_t; /* Do not use stdio */ +#define NO_FILE (-1) /* in memory compression */ + + +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/ +#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +extern int decrypt; /* flag to turn on decryption */ +extern int exit_code; /* program exit code */ +extern int verbose; /* be verbose (-v) */ +extern int quiet; /* be quiet (-q) */ +extern int test; /* check .z file integrity */ +extern int to_stdout; /* output to stdout (-c) */ +extern int save_orig_name; /* set if original name must be saved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0)) +#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1)) + +/* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} +#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ + flush_window();} + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ if (outcnt < OUTBUFSIZ-2) { \ + outbuf[outcnt++] = (uch) ((w) & 0xff); \ + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ + } else { \ + put_byte((uch)((w) & 0xff)); \ + put_byte((uch)((ush)(w) >> 8)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ + +/* Macros for getting two-byte and four-byte header values */ +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#define WARN(msg) {if (!quiet) fprintf msg ; \ + if (exit_code == OK) exit_code = WARNING;} + +local void do_exit(int exitcode); + + /* in zip.c: */ +extern int zip OF((int in, int out)); +extern int file_read OF((char *buf, unsigned size)); + + /* in unzip.c */ +extern int unzip OF((int in, int out)); +extern int check_zipfile OF((int in)); + + /* in unpack.c */ +extern int unpack OF((int in, int out)); + + /* in unlzh.c */ +extern int unlzh OF((int in, int out)); + + /* in gzip.c */ +RETSIGTYPE abort_gzip OF((void)); + + /* in deflate.c */ +void lm_init OF((ush *flags)); +ulg deflate OF((void)); + + /* in trees.c */ +void ct_init OF((ush *attr, int *method)); +int ct_tally OF((int dist, int lc)); +ulg flush_block OF((char *buf, ulg stored_len, int eof)); + + /* in bits.c */ +void bi_init OF((file_t zipfile)); +void send_bits OF((int value, int length)); +unsigned bi_reverse OF((unsigned value, int length)); +void bi_windup OF((void)); +void copy_block OF((char *buf, unsigned len, int header)); +extern int (*read_buf) OF((char *buf, unsigned size)); + + /* in util.c: */ +extern int copy OF((int in, int out)); +extern ulg updcrc OF((uch *s, unsigned n)); +extern void clear_bufs OF((void)); +extern int fill_inbuf OF((int eof_ok)); +extern void flush_outbuf OF((void)); +extern void flush_window OF((void)); +extern void write_buf OF((int fd, voidp buf, unsigned cnt)); +extern char *strlwr OF((char *s)); +extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); +extern void error OF((char *m)); +extern void warn OF((char *a, char *b)); +extern void read_error OF((void)); +extern void write_error OF((void)); +extern void display_ratio OF((long num, long den, FILE *file)); +extern voidp xmalloc OF((unsigned int size)); + + /* in inflate.c */ +extern int inflate OF((void)); +/* lzw.h -- define the lzw functions. + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if !defined(OF) && defined(lint) +# include "gzip.h" +#endif + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#define BLOCK_MODE 0x80 +/* Block compression: if table is full and compression rate is dropping, + * clear the dictionary. + */ + +#define LZW_RESERVED 0x60 /* reserved bits */ + +#define CLEAR 256 /* flush the dictionary */ +#define FIRST (CLEAR+1) /* first free entry */ + +extern int maxbits; /* max bits per code for LZW */ +extern int block_mode; /* block compress mode -C compatible with 2.0 */ + +/* revision.h -- define the version number + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#define VERSION "1.2.4" +#define PATCHLEVEL 0 +#define REVDATE "18 Aug 93" + +/* This version does not support compression into old compress format: */ +#ifdef LZW +# undef LZW +#endif + +/* $Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + +/* $Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +#if defined(__OS2__) && !defined(OS2) +# define OS2 +#endif + +#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */ +# undef MSDOS +#endif + +#ifdef MSDOS +# ifdef __GNUC__ + /* DJGPP version 1.09+ on MS-DOS. + * The DJGPP 1.09 stat() function must be upgraded before gzip will + * fully work. + * No need for DIRENT, since defines POSIX_SOURCE which + * implies DIRENT. + */ +# define near +# else +# define MAXSEG_64K +# ifdef __TURBOC__ +# define NO_OFF_T +# ifdef __BORLANDC__ +# define DIRENT +# else +# define NO_UTIME +# endif +# else /* MSC */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# endif +# endif +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define NO_SIZE_CHECK +# define casemap(c) tolow(c) /* Force file names to lower case */ +# include +# define OS_CODE 0x00 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# if !defined(NO_ASM) && !defined(ASMV) +# define ASMV +# endif +#else +# define near +#endif + +#ifdef OS2 +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# ifdef OS2FAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) +# endif +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# include +# define OS_CODE 0x06 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# ifdef _MSC_VER +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define MAXSEG_64K +# undef near +# define near _near +# endif +# ifdef __EMX__ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define DIRENT +# define EXPAND(argc,argv) \ + {_response(&argc, &argv); _wildcard(&argc, &argv);} +# endif +# ifdef __BORLANDC__ +# define DIRENT +# endif +# ifdef __ZTC__ +# define NO_DIR +# define NO_UTIME_H +# include +# define EXPAND(argc,argv) \ + {response_expand(&argc, &argv);} +# endif +#endif + +#ifdef WIN32 /* Windows NT */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# include +# include +# ifdef NTFAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) /* Force file names to lower case */ +# endif +# define OS_CODE 0x0b +#endif + +#ifdef MSDOS +# ifdef __TURBOC__ +# include +# define DYN_ALLOC + /* Turbo C 2.0 does not accept static allocations of large arrays */ + void * fcalloc (unsigned items, unsigned size); + void fcfree (void *ptr); +# else /* MSC */ +# include +# define fcalloc(nitems,itemsize) halloc((long)(nitems),(itemsize)) +# define fcfree(ptr) hfree(ptr) +# endif +#else +# ifdef MAXSEG_64K +# define fcalloc(items,size) calloc((items),(size)) +# else +# define fcalloc(items,size) malloc((size_t)(items)*(size_t)(size)) +# endif +# define fcfree(ptr) free(ptr) +#endif + +#if defined(VAXC) || defined(VMS) +# define PATH_SEP ']' +# define PATH_SEP2 ':' +# define SUFFIX_SEP ';' +# define NO_MULTIPLE_DOTS +# define Z_SUFFIX "-gz" +# define RECORD_IO 1 +# define casemap(c) tolow(c) +# define OS_CODE 0x02 +# define OPTIONS_VAR "GZIP_OPT" +# define STDC_HEADERS +# define NO_UTIME +# define EXPAND(argc,argv) vms_expand_args(&argc,&argv); +# include +# define unlink delete +# ifdef VAXC +# define NO_FCNTL_H +# include +# endif +#endif + +#ifdef AMIGA +# define PATH_SEP2 ':' +# define STDC_HEADERS +# define OS_CODE 0x01 +# define ASMV +# ifdef __GNUC__ +# define DIRENT +# define HAVE_UNISTD_H +# else /* SASC */ +# define NO_STDIN_FSTAT +# define SYSDIR +# define NO_SYMLINK +# define NO_CHOWN +# define NO_FCNTL_H +# include /* for read() and write() */ +# define direct dirent + extern void _expand_args(int *argc, char ***argv); +# define EXPAND(argc,argv) _expand_args(&argc,&argv); +# undef O_BINARY /* disable useless --ascii option */ +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# ifndef STDC_HEADERS +# define STDC_HEADERS +# define HAVE_UNISTD_H +# define DIRENT +# endif +# define ASMV +# define OS_CODE 0x05 +# ifdef TOSFS +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define NO_SYMLINK +# endif +#endif + +#ifdef MACOS +# define PATH_SEP ':' +# define DYN_ALLOC +# define PROTO +# define NO_STDIN_FSTAT +# define NO_CHOWN +# define NO_UTIME +# define chmod(file, mode) (0) +# define OPEN(name, flags, mode) open(name, flags) +# define OS_CODE 0x07 +# ifdef MPW +# define isatty(fd) ((fd) <= 2) +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define PATH_SEP '>' +# define STDC_HEADERS +# define NO_MEMORY_H +# define NO_UTIME_H +# define NO_UTIME +# define NO_CHOWN +# define NO_STDIN_FSTAT +# define NO_SIZE_CHECK +# define NO_SYMLINK +# define RECORD_IO 1 +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define put_char(c) put_byte((c) & 0x7F) +# define get_char(c) ascii2pascii(get_byte()) +# define OS_CODE 0x0F /* temporary, subject to change */ +# ifdef SIGTERM +# undef SIGTERM /* We don't want a signal handler for SIGTERM */ +# endif +#endif + +#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */ +# define NOMEMCPY /* problem with overlapping copies */ +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifndef unix +# define NO_ST_INO /* don't rely on inode numbers */ +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef PATH_SEP +# define PATH_SEP '/' +#endif + +#ifndef casemap +# define casemap(c) (c) +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "GZIP" +#endif + +#ifndef Z_SUFFIX +# define Z_SUFFIX ".gz" +#endif + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + +#ifndef MAKE_LEGAL_NAME +# ifdef NO_MULTIPLE_DOTS +# define MAKE_LEGAL_NAME(name) make_simple_name(name) +# else +# define MAKE_LEGAL_NAME(name) +# endif +#endif + +#ifndef MIN_PART +# define MIN_PART 3 + /* keep at least MIN_PART chars between dots in a file name. */ +#endif + +#ifndef EXPAND +# define EXPAND(argc,argv) +#endif + +#ifndef RECORD_IO +# define RECORD_IO 0 +#endif + +#ifndef SET_BINARY_MODE +# define SET_BINARY_MODE(fd) +#endif + +#ifndef OPEN +# define OPEN(name, flags, mode) open(name, flags, mode) +#endif + +#ifndef get_char +# define get_char() get_byte() +#endif + +#ifndef put_char +# define put_char(c) put_byte(c) +#endif +/* bits.c -- output variable-length bit strings + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + + +/* + * PURPOSE + * + * Output variable-length bit strings. Compression can be done + * to a file or to memory. (The latter is not supported in this version.) + * + * DISCUSSION + * + * The PKZIP "deflate" file format interprets compressed file data + * as a sequence of bits. Multi-bit strings in the file may cross + * byte boundaries without restriction. + * + * The first bit of each byte is the low-order bit. + * + * The routines in this file allow a variable-length bit value to + * be output right-to-left (useful for literal values). For + * left-to-right output (useful for code strings from the tree routines), + * the bits must have been reversed first with bi_reverse(). + * + * For in-memory compression, the compressed bit stream goes directly + * into the requested output buffer. The input data is read in blocks + * by the mem_read() function. The buffer is limited to 64K on 16 bit + * machines. + * + * INTERFACE + * + * void bi_init (FILE *zipfile) + * Initialize the bit string routines. + * + * void send_bits (int value, int length) + * Write out a bit string, taking the source bits right to + * left. + * + * int bi_reverse (int value, int length) + * Reverse the bits of a bit string, taking the source bits left to + * right and emitting them right to left. + * + * void bi_windup (void) + * Write out any remaining bits in an incomplete byte. + * + * void copy_block(char *buf, unsigned len, int header) + * Copy a stored block to the zip file, storing first the length and + * its one's complement if requested. + * + */ + +#ifdef DEBUG +# include +#endif + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Local data used by the "bit string" routines. + */ + +local file_t zfile; /* output gzip file */ + +local unsigned short bi_buf; +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +local int bi_valid; +/* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +int (*read_buf) OF((char *buf, unsigned size)); +/* Current input function. Set to mem_read for in-memory compression */ + +#ifdef DEBUG + ulg bits_sent; /* bit length of the compressed data */ +#endif + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (zipfile) + file_t zipfile; /* output zip file, NO_FILE for in-memory compression */ +{ + zfile = zipfile; + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = 0L; +#endif + + /* Set the defaults for file compression. They are set by memcompress + * for in-memory compression. + */ + if (zfile != NO_FILE) { + read_buf = file_read; + } +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(value, length) + int value; /* value to send */ + int length; /* number of bits */ +{ +#ifdef DEBUG + Tracev((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + bits_sent += (ulg)length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (bi_valid > (int)Buf_size - length) { + bi_buf |= (value << bi_valid); + put_short(bi_buf); + bi_buf = (ush)value >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= value << bi_valid; + bi_valid += length; + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup() +{ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte(bi_buf); + } + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = (bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(buf, len, header) + char *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_short((ush)len); + put_short((ush)~len); +#ifdef DEBUG + bits_sent += 2*16; +#endif + } +#ifdef DEBUG + bits_sent += (ulg)len<<3; +#endif + while (len--) { +#ifdef CRYPT + int t; + if (key) zencode(*buf, t); +#endif + put_byte(*buf++); + } +} +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Identify new text as repetitions of old text within a fixed- + * length sliding window trailing behind the new text. + * + * DISCUSSION + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many info-zippers for bug reports and testing. + * + * REFERENCES + * + * APPNOTE.TXT documentation file in PKZIP 1.93a distribution. + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + * INTERFACE + * + * void lm_init (int pack_level, ush *flags) + * Initialize the "longest match" routines for a new file + * + * ulg deflate (void) + * Processes a new input file and return its compressed length. Sets + * the compressed length, crc, deflate flags and internal file + * attributes. + */ + +#include + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Configuration parameters + */ + +/* Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ + +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and + * window with tab_suffix. Check that we can do this: + */ +#if (WSIZE<<1) > (1< BITS-1 + error: cannot overlay head with tab_prefix1 +#endif + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + */ + +unsigned int near prev_length; +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + unsigned near strstart; /* start of string to insert */ + unsigned near match_start; /* start of matching string */ +local int eofile; /* flag set at end of input file */ +local unsigned lookahead; /* number of valid bytes ahead in window */ + +unsigned near max_chain_length; +/* To speed up deflation, hash chains are never searched beyond this length. + * A higher limit improves compression ratio but degrades the speed. + */ + +local unsigned int max_lazy_match; +/* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +#define max_insert_length max_lazy_match +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + +unsigned near good_match; +/* Use a faster search when the previous match is longer than this */ + + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + +typedef struct config { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; +} config; + +#ifdef FULL_SEARCH +# define nice_match MAX_MATCH +#else + int near nice_match; /* Stop searching when current match exceeds this */ +#endif + +local config configuration_table = +/* 9 */ {32, 258, 258, 4096}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +/* =========================================================================== + * Prototypes for local functions. + */ +local void fill_window OF((void)); + + int longest_match OF((IPos cur_match)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ +#endif + +#ifdef DEBUG +local void check_match OF((IPos start, IPos match, int length)); +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)<= 1 + */ +#ifndef ASMV +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +int longest_match(cur_match) + IPos cur_match; /* current match */ +{ + unsigned chain_length = max_chain_length; /* max hash chain length */ + register uch *scan = window + strstart; /* current string */ + register uch *match; /* matched string */ + register int len; /* length of current match */ + int best_len = prev_length; /* best match length so far */ + IPos limit = strstart > (IPos)MAX_DIST ? strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +#if HASH_BITS < 8 || MAX_MATCH != 258 + error: Code too clever +#endif + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register uch *strend = window + strstart + MAX_MATCH - 1; + register ush scan_start = *(ush*)scan; + register ush scan_end = *(ush*)(scan+best_len-1); +#else + register uch *strend = window + strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; +#endif + + /* Do not waste too much time if we already have a good match: */ + if (prev_length >= good_match) { + chain_length >>= 2; + } + Assert(strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(cur_match < strstart, "no future"); + match = window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ush*)(match+best_len-1) != scan_end || + *(ush*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + scan++, match++; + do { + } while (*(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= window+(unsigned)(window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ush*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} +#endif /* ASMV */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(start, match, length) + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (memcmp((char*)window + match, + (char*)window + start, length) != EQUAL) { + fprintf(stderr, + " start %d, match %d, length %d\n", + start, match, length); + error("invalid match"); + } + if (verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +local void fill_window() +{ + register unsigned n, m; + unsigned more = (unsigned)(window_size - (ulg)lookahead - (ulg)strstart); + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (strstart >= WSIZE+MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(window_size == (ulg)2*WSIZE, "no sliding with BIG_MEM"); + + memcpy((char*)window, (char*)window+WSIZE, (unsigned)WSIZE); + match_start -= WSIZE; + strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = prev[n]; + prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!eofile) { + n = read_buf((char*)window+strstart+lookahead, more); + if (n == 0 || n == (unsigned)EOF) { + eofile = 1; + } else { + lookahead += n; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(eof) \ + flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \ + (char*)NULL, (long)strstart - block_start, (eof)) + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate() +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ +#ifdef DEBUG + extern long isize; /* byte length of input file, for debug only */ +#endif + + /* Process the input block. */ + while (lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + prev_length = match_length, prev_match = match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && prev_length < max_lazy_match && + strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match (hash_head); + /* longest_match() sets match_start */ + if (match_length > lookahead) match_length = lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && strstart-match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (prev_length >= MIN_MATCH && match_length <= prev_length) { + + check_match(strstart-1, prev_match, prev_length); + + flush = ct_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + lookahead -= prev_length-1; + prev_length -= 2; + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH-1; + strstart++; + if (flush) FLUSH_BLOCK(0), block_start = strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c",window[strstart-1])); + if (ct_tally (0, window[strstart-1])) { + FLUSH_BLOCK(0), block_start = strstart; + } + strstart++; + lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + strstart++; + lookahead--; + } + Assert (strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) fill_window(); + } + if (match_available) ct_tally (0, window[strstart-1]); + + return FLUSH_BLOCK(1); /* eof */ +} +/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +/* Compress files with zip algorithm and 'compress' interface. + * See usage() and help() functions below for all options. + * Outputs: + * file.gz: compressed file with same mode, owner, and utimes + * or stdout with -c option or if stdin used as input. + * If the output file name had to be truncated, the original name is kept + * in the compressed file. + * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * + * For the meaning of all compilation flags, see comments in Makefile.in. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include +#include +#include + + /* configuration */ + +#ifdef NO_TIME_H +# include +#else +# include +#endif + +#ifndef NO_FCNTL_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#else + extern int errno; +#endif + +#if defined(DIRENT) +# include + typedef struct dirent dir_type; +# define NLENGTH(dirent) ((int)strlen((dirent)->d_name)) +# define DIR_OPT "DIRENT" +#else +# define NLENGTH(dirent) ((dirent)->d_namlen) +# ifdef SYSDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSDIR" +# else +# ifdef SYSNDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSNDIR" +# else +# ifdef NDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "NDIR" +# else +# define NO_DIR +# define DIR_OPT "NO_DIR" +# endif +# endif +# endif +#endif + +#ifndef NO_UTIME +# ifndef NO_UTIME_H +# include +# define TIME_OPT "UTIME" +# else +# ifdef HAVE_SYS_UTIME_H +# include +# define TIME_OPT "SYS_UTIME" +# else + struct utimbuf { + time_t actime; + time_t modtime; + }; +# define TIME_OPT "" +# endif +# endif +#else +# define TIME_OPT "NO_UTIME" +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +typedef RETSIGTYPE (*sig_type) OF((int)); + +#ifndef O_BINARY +# define O_BINARY 0 /* creation mode for open() */ +#endif + +#ifndef O_CREAT + /* Pure BSD system? */ +# include +# ifndef O_CREAT +# define O_CREAT FCREAT +# endif +# ifndef O_EXCL +# define O_EXCL FEXCL +# endif +#endif + +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif +#define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifdef NO_OFF_T + typedef long off_t; + off_t lseek OF((int fd, off_t offset, int whence)); +#endif + +/* Separator for file name parts (see shorten_name()) */ +#ifdef NO_MULTIPLE_DOTS +# define PART_SEP "-" +#else +# define PART_SEP "." +#endif + + /* global buffers */ + +DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA); +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); +DECLARE(ush, d_buf, DIST_BUFSIZE); +DECLARE(uch, window, 2L*WSIZE); +#ifndef MAXSEG_64K + DECLARE(ush, tab_prefix, 1L< + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + + +local int near extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local int near extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local int near extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#if LIT_BUFSIZE > INBUFSIZ + error cannot overlay l_buf and inbuf +#endif + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== + * Local data + */ + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +local ct_data near dyn_ltree[HEAP_SIZE]; /* literal and length tree */ +local ct_data near dyn_dtree[2*D_CODES+1]; /* distance tree */ + +local ct_data near static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + +local ct_data near static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +local ct_data near bl_tree[2*BL_CODES+1]; +/* Huffman tree for the bit lengths */ + +typedef struct tree_desc { + ct_data near *dyn_tree; /* the dynamic tree */ + ct_data near *static_tree; /* corresponding static tree or NULL */ + int near *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +local tree_desc near l_desc = +{dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; + +local tree_desc near d_desc = +{dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; + +local tree_desc near bl_desc = +{bl_tree, (ct_data near *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; + + +local ush near bl_count[MAX_BITS+1]; +/* number of codes at each bit length for an optimal tree */ + +local uch near bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +local int near heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ +local int heap_len; /* number of elements in the heap */ +local int heap_max; /* element of largest frequency */ +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + +local uch near depth[2*L_CODES+1]; +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local uch dist_code[512]; +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +local int near base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int near base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#define l_buf inbuf +/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */ + +/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */ + +local uch near flag_buf[(LIT_BUFSIZE/8)]; +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + +local unsigned last_lit; /* running index in l_buf */ +local unsigned last_dist; /* running index in d_buf */ +local unsigned last_flags; /* running index in flag_buf */ +local uch flags; /* current flags not yet saved in flag_buf */ +local uch flag_bit; /* current bit used in flags */ +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + +local ulg opt_len; /* bit length of current block with optimal trees */ +local ulg static_len; /* bit length of current block with static trees */ + +local ulg compressed_len; /* total bit length of compressed file */ + +local ulg input_len; /* total byte length of input file */ +/* input_len is for debugging only since we can get it by other means. */ + +ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */ +int *file_method; /* pointer to DEFLATE or STORE */ + +#ifdef DEBUG +extern ulg bits_sent; /* bit length of the compressed data */ +extern long isize; /* byte length of input file */ +#endif + +extern long block_start; /* window offset of current block */ +extern unsigned near strstart; /* window offset of current string */ + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void init_block OF((void)); +local void pqdownheap OF((ct_data near *tree, int k)); +local void gen_bitlen OF((tree_desc near *desc)); +local void gen_codes OF((ct_data near *tree, int max_code)); +local void build_tree OF((tree_desc near *desc)); +local void scan_tree OF((ct_data near *tree, int max_code)); +local void send_tree OF((ct_data near *tree, int max_code)); +local int build_bl_tree OF((void)); +local void send_all_trees OF((int lcodes, int dcodes, int blcodes)); +local void compress_block OF((ct_data near *ltree, ct_data near *dtree)); +local void set_file_type OF((void)); + + +#ifndef DEBUG +# define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(c, tree) \ + { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(attr, methodp) + ush *attr; /* pointer to internal file attribute */ + int *methodp; /* pointer to compression method */ +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + file_type = attr; + file_method = methodp; + compressed_len = input_len = 0L; + + if (static_dtree[0].Len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data near *)static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block() +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) bl_tree[n].Freq = 0; + + dyn_ltree[END_BLOCK].Freq = 1; + opt_len = static_len = 0L; + last_lit = last_dist = last_flags = 0; + flags = 0; flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = heap[SMALLEST]; \ + heap[SMALLEST] = heap[heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(tree, k) + ct_data near *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = heap[k]; + int j = k << 1; /* left son of k */ + while (j <= heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < heap_len && smaller(tree, heap[j+1], heap[j])) j++; + + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, heap[j])) break; + + /* Exchange v with the smallest son */ + heap[k] = heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(desc) + tree_desc near *desc; /* the tree descriptor */ +{ + ct_data near *tree = desc->dyn_tree; + int near *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data near *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap[heap_max]].Len = 0; /* root of the heap */ + + for (h = heap_max+1; h < HEAP_SIZE; h++) { + n = heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + opt_len += (ulg)f * (bits + xbits); + if (stree) static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (bl_count[bits] == 0) bits--; + bl_count[bits]--; /* move one leaf down the tree */ + bl_count[bits+1] += 2; /* move one overflow item as its brother */ + bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = bl_count[bits]; + while (n != 0) { + m = heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + opt_len += ((long)bits-(long)tree[m].Len)*(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code) + ct_data near *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + ct_data near *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len = 0, heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + heap[++heap_len] = max_code = n; + depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (heap_len < 2) { + int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0); + tree[new].Freq = 1; + depth[new] = 0; + opt_len--; if (stree) static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = heap_len/2; n >= 1; n--) pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = heap[SMALLEST]; /* m = node of next least frequency */ + + heap[--heap_max] = n; /* keep the nodes sorted by frequency */ + heap[--heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + depth[node] = (uch) (MAX(depth[n], depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (heap_len >= 2); + + heap[--heap_max] = heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc near *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data near *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +local void scan_tree (tree, max_code) + ct_data near *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) bl_tree[curlen].Freq++; + bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + bl_tree[REPZ_3_10].Freq++; + } else { + bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (tree, max_code) + ct_data near *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(REP_3_6, bl_tree); send_bits(count-3, 2); + + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); send_bits(count-3, 3); + + } else { + send_code(REPZ_11_138, bl_tree); send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree() +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree((ct_data near *)dyn_ltree, l_desc.max_code); + scan_tree((ct_data near *)dyn_dtree, d_desc.max_code); + + /* Build the bit length tree: */ + build_tree((tree_desc near *)(&bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(lcodes, dcodes, blcodes) + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", bits_sent)); + + send_tree((ct_data near *)dyn_ltree, lcodes-1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", bits_sent)); + + send_tree((ct_data near *)dyn_dtree, dcodes-1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", bits_sent)); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +ulg flush_block(buf, stored_len, eof) + char *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*file_type == (ush)UNKNOWN) set_file_type(); + + /* Construct the literal and distance trees */ + build_tree((tree_desc near *)(&l_desc)); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len)); + + build_tree((tree_desc near *)(&d_desc)); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (opt_len+3+7)>>3; + static_lenb = (static_len+3+7)>>3; + input_len += stored_len; /* for debugging only */ + + Trace((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, opt_len, static_lenb, static_len, stored_len, + last_lit, last_dist)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +#ifdef FORCE_METHOD +#else + if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) { +#endif + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (char*)0) error ("block vanished"); + + copy_block(buf, (unsigned)stored_len, 0); /* without header */ + compressed_len = stored_len << 3; + *file_method = STORED; + +#ifdef FORCE_METHOD +#else + } else if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK<<1)+eof, 3); /* send block type */ + compressed_len = (compressed_len + 3 + 7) & ~7L; + compressed_len += (stored_len + 4) << 3; + + copy_block(buf, (unsigned)stored_len, 1); /* with header */ + +#ifdef FORCE_METHOD +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits((STATIC_TREES<<1)+eof, 3); + compress_block((ct_data near *)static_ltree, (ct_data near *)static_dtree); + compressed_len += 3 + static_len; + } else { + send_bits((DYN_TREES<<1)+eof, 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block((ct_data near *)dyn_ltree, (ct_data near *)dyn_dtree); + compressed_len += 3 + opt_len; + } + Assert (compressed_len == bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + Assert (input_len == isize, "bad input size"); + bi_windup(); + compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", compressed_len>>3, + compressed_len-7*eof)); + + return compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (dist, lc) + int dist; /* distance of matched string */ + int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + l_buf[last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + dyn_dtree[d_code(dist)].Freq++; + + d_buf[last_dist++] = (ush)dist; + flags |= flag_bit; + } + flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((last_lit & 7) == 0) { + flag_buf[last_flags++] = flags; + flags = 0, flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)last_lit*8L; + ulg in_length = (ulg)strstart-block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)dyn_dtree[dcode].Freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + last_lit, last_dist, in_length, out_length, + 100L - out_length*100L/in_length)); + if (last_dist < last_lit/2 && out_length < in_length/2) return 1; + } + return (last_lit == LIT_BUFSIZE-1 || last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(ltree, dtree) + ct_data near *ltree; /* literal tree */ + ct_data near *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (last_lit != 0) do { + if ((lx & 7) == 0) flag = flag_buf[fx++]; + lc = l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < last_lit); + + send_code(END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_file_type() +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += dyn_ltree[n++].Freq; + *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII; + if (*file_type == BINARY && translate_eol) { + warn("-l used on binary file", ""); + } +} +/* util.c -- utility functions for gzip support + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifndef NO_FCNTL_H +# include +#endif + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#else + extern int errno; +#endif + +extern ulg crc_32_tab[]; /* crc table, defined below */ + +/* =========================================================================== + * Copy input to output unchanged: zcat == cat with --force. + * IN assertion: insize bytes have already been read in inbuf. + */ +int copy(in, out) + int in, out; /* input and output file descriptors */ +{ + errno = 0; + while (insize != 0 && (int)insize != EOF) { + write_buf(out, (char*)inbuf, insize); + bytes_out += insize; + insize = read(in, (char*)inbuf, INBUFSIZ); + } + if ((int)insize == EOF && errno != 0) { + read_error(); + } + bytes_in = bytes_out; + return OK; +} + +/* ======================================================================== + * Put string s in lower case, return s. + */ +char *strlwr(s) + char *s; +{ + char *t; + for (t = s; *t; t++) *t = tolow(*t); + return s; +} + +#if defined(NO_STRING_H) && !defined(STDC_HEADERS) + +/* Provide missing strspn and strcspn functions. */ + +# ifndef __STDC__ +# define const +# endif + +int strspn OF((const char *s, const char *accept)); +int strcspn OF((const char *s, const char *reject)); + +/* ======================================================================== + * Return the length of the maximum initial segment + * of s which contains only characters in accept. + */ +int strspn(s, accept) + const char *s; + const char *accept; +{ + register const char *p; + register const char *a; + register int count = 0; + + for (p = s; *p != '\0'; ++p) { + for (a = accept; *a != '\0'; ++a) { + if (*p == *a) break; + } + if (*a == '\0') return count; + ++count; + } + return count; +} + +/* ======================================================================== + * Return the length of the maximum inital segment of s + * which contains no characters from reject. + */ +int strcspn(s, reject) + const char *s; + const char *reject; +{ + register int count = 0; + + while (*s != '\0') { + if (strchr(reject, *s++) != NULL) return count; + ++count; + } + return count; +} + +#endif /* NO_STRING_H */ + +/* ======================================================================== + * Add an environment variable (if any) before argv, and update argc. + * Return the expanded environment variable to be freed later, or NULL + * if no options were added to argv. + */ +#define SEPARATOR " \t" /* separators in env variable */ + +char *add_envopt(argcp, argvp, env) + int *argcp; /* pointer to argc */ + char ***argvp; /* pointer to argv */ + char *env; /* name of environment variable */ +{ + char *p; /* running pointer through env variable */ + char **oargv; /* runs through old argv array */ + char **nargv; /* runs through new argv array */ + int oargc = *argcp; /* old argc */ + int nargc = 0; /* number of arguments in env variable */ + + env = (char*)getenv(env); + if (env == NULL) return NULL; + + p = (char*)xmalloc(strlen(env)+1); + env = strcpy(p, env); /* keep env variable intact */ + + for (p = env; *p; nargc++ ) { /* move through env */ + p += strspn(p, SEPARATOR); /* skip leading separators */ + if (*p == '\0') break; + + p += strcspn(p, SEPARATOR); /* find end of word */ + if (*p) *p++ = '\0'; /* mark it */ + } + if (nargc == 0) { + free(env); + return NULL; + } + *argcp += nargc; + /* Allocate the new argv array, with an extra element just in case + * the original arg list did not end with a NULL. + */ + nargv = (char**)calloc(*argcp+1, sizeof(char *)); + if (nargv == NULL) error("out of memory"); + oargv = *argvp; + *argvp = nargv; + + /* Copy the program name first */ + if (oargc-- < 0) error("argc<=0"); + *(nargv++) = *(oargv++); + + /* Then copy the environment args */ + for (p = env; nargc > 0; nargc--) { + p += strspn(p, SEPARATOR); /* skip separators */ + *(nargv++) = p; /* store start */ + while (*p++) ; /* skip over word */ + } + + /* Finally copy the old args and add a NULL (usual convention) */ + while (oargc--) *(nargv++) = *(oargv++); + *nargv = NULL; + return env; +} +/* ======================================================================== + * Display compression ratio on the given stream on 6 characters. + */ +void display_ratio(num, den, file) + long num; + long den; + FILE *file; +{ + long ratio; /* 1000 times the compression ratio */ + + if (den == 0) { + ratio = 0; /* no compression */ + } else if (den < 2147483L) { /* (2**31 -1)/1000 */ + ratio = 1000L*num/den; + } else { + ratio = num/(den/1000L); + } + if (ratio < 0) { + putc('-', file); + ratio = -ratio; + } else { + putc(' ', file); + } + fprintf(file, "%2ld.%1ld%%", ratio / 10L, ratio % 10L); +} + + +/* zip.c -- compress files to the gzip or pkzip format + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifndef NO_FCNTL_H +# include +#endif + +local ulg crc; /* crc on uncompressed file data */ +long header_bytes; /* number of bytes in gzip header */ + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + * The variables time_stamp and save_orig_name are initialized. + */ +int zip(in, out) + int in, out; /* input and output file descriptors */ +{ + uch flags = 0; /* general purpose bit flags */ + ush attr = 0; /* ascii/binary flag */ + ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + + ifd = in; + ofd = out; + outcnt = 0; + + /* Write the header to the gzip file. See algorithm.doc for the format */ + + method = DEFLATED; + put_byte(GZIP_MAGIC[0]); /* magic header */ + put_byte(GZIP_MAGIC[1]); + put_byte(DEFLATED); /* compression method */ + + put_byte(flags); /* general flags */ + put_long(time_stamp); + + /* Write deflated file to zip file */ + crc = updcrc(0, 0); + + bi_init(out); + ct_init(&attr, &method); + lm_init(&deflate_flags); + + put_byte((uch)deflate_flags); /* extra flags */ + put_byte(OS_CODE); /* OS identifier */ + + header_bytes = (long)outcnt; + + (void)deflate(); + + /* Write the crc and uncompressed size */ + put_long(crc); + put_long(isize); + header_bytes += 2*sizeof(long); + + flush_outbuf(); + return OK; +} + + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +int file_read(buf, size) + char *buf; + unsigned size; +{ + unsigned len; + + Assert(insize == 0, "inbuf not empty"); + + len = read(ifd, buf, size); + if (len == (unsigned)(-1) || len == 0) return (int)len; + + crc = updcrc((uch*)buf, len); + isize += (ulg)len; + return (int)len; +} +#endif diff --git a/halt.c b/halt.c new file mode 100644 index 000000000..7f3ccf966 --- /dev/null +++ b/halt.c @@ -0,0 +1,12 @@ +#include "internal.h" +#include + +const char halt_usage[] = "halt\n" +"\n\t" +"\thalt the system.\n"; + +extern int +halt_main(struct FileInfo * i, int argc, char * * argv) +{ + return kill(1, SIGUSR1); +} diff --git a/init.c b/init.c new file mode 100644 index 000000000..4771722b9 --- /dev/null +++ b/init.c @@ -0,0 +1,438 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char init_usage[] = "Used internally by the system."; +char console[16] = ""; +const char * default_console = "/dev/tty1"; +char * first_terminal = NULL; +const char * second_terminal = "/dev/tty2"; +const char log[] = "/dev/tty3"; +char * term_ptr = NULL; + +static void +message(const char * terminal, const char * pattern, ...) +{ + int fd; + FILE * con = 0; + va_list arguments; + + /* + * Open the console device each time a message is printed. If the user + * has switched consoles, the open will get the new console. If we kept + * the console open, we'd always print to the same one. + */ + if ( ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0) + || ((con = fdopen(fd, "w")) == NULL) ) + return; + + va_start(arguments, pattern); + vfprintf(con, pattern, arguments); + va_end(arguments); + fclose(con); +} + +static int +waitfor(int pid) +{ + int status; + int wpid; + + message(log, "Waiting for process %d.\n", pid); + while ( (wpid = wait(&status)) != pid ) { + if ( wpid > 0 ) { + message( + log + ,"pid %d exited, status=%x.\n" + ,wpid + ,status); + } + } + return wpid; +} + +static int +run( + const char * program +,const char * const * arguments +,const char * terminal +,int get_enter) +{ + static const char control_characters[] = { + '\003', + '\034', + '\177', + '\025', + '\004', + '\0', + '\1', + '\0', + '\021', + '\023', + '\032', + '\0', + '\022', + '\017', + '\027', + '\026', + '\0' + }; + + static char * environment[] = { + "HOME=/", + "PATH=/bin:/sbin:/usr/bin:/usr/sbin", + "SHELL=/bin/sh", + 0, + "USER=root", + 0 + }; + + static const char press_enter[] = + "Please press Enter to activate this console. "; + + int pid; + + environment[3]=term_ptr; + + pid = fork(); + if ( pid == 0 ) { + struct termios t; + const char * const * arg; + + close(0); + close(1); + close(2); + setsid(); + + open(terminal, O_RDWR); + dup(0); + dup(0); + tcsetpgrp(0, getpgrp()); + + tcgetattr(0, &t); + memcpy(t.c_cc, control_characters, sizeof(control_characters)); + t.c_line = 0; + t.c_iflag = ICRNL|IXON|IXOFF; + t.c_oflag = OPOST|ONLCR; + t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN; + tcsetattr(0, TCSANOW, &t); + + if ( get_enter ) { + /* + * Save memory by not exec-ing anything large (like a shell) + * before the user wants it. This is critical if swap is not + * enabled and the system has low memory. Generally this will + * be run on the second virtual console, and the first will + * be allowed to start a shell or the installation system. + */ + char c; + write(1, press_enter, sizeof(press_enter) - 1); + read(0, &c, 1); + } + + message(log, "Executing "); + arg = arguments; + while ( *arg != 0 ) + message(log, "%s ", *arg++); + message(log, "\n"); + + execve(program, (char * *)arguments, (char * *)environment); + message(log, "%s: could not execute: %s.\r\n", program, strerror(errno)); + exit(-1); + } + return pid; +} + +static int +mem_total() +{ + char s[80]; + char *p; + FILE *f; + const char pattern[]="MemTotal:"; + + f=fopen("/proc/meminfo","r"); + while (NULL != fgets(s,79,f)) { + p=strstr(s, pattern); + if (NULL != p) { + fclose(f); + return(atoi(p+strlen(pattern))); + } + } + return -1; +} + +static void +set_free_pages() +{ + char s[80]; + FILE *f; + + f=fopen("/proc/sys/vm/freepages","r"); + fgets(s,79,f); + if (atoi(s) < 32) { + fclose(f); + f=fopen("/proc/sys/vm/freepages","w"); + fprintf(f,"30\t40\t50\n"); + printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n"); + } + fclose(f); +} + +static void +shutdown_system(int do_reboot) +{ + static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0}; + + sync(); + /* Allow Ctrl-Alt-Del to reboot system. */ + reboot(RB_ENABLE_CAD); + + /* Send signals to every process _except_ pid 1 */ + message(console, "Sending SIGHUP to all processes.\r\n"); + kill(-1, SIGHUP); + sleep(2); + sync(); + message(console, "Sending SIGKILL to all processes.\r\n"); + kill(-1, SIGKILL); + sleep(1); + waitfor(run("/bin/umount", umount_args, console, 0)); + sync(); + bdflush(1, 0); + sync(); + reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM); + exit(0); +} + +static void +halt_signal(int sig) +{ + shutdown_system(0); +} + +static void +reboot_signal(int sig) +{ + shutdown_system(1); +} + +static void +exit_signal(int sig) +{ + + /* initrd doesn't work anyway */ + + shutdown_system(1); + + /* This is used on the initial ramdisk */ + + /* message(log, "Init exiting."); + exit(0); + */ +} + +void +configure_terminals( int serial_cons ); + +extern int +init_main(struct FileInfo * i, int argc, char * * argv) +{ + static const char * const rc = "etc/rc"; + const char * arguments[100]; + int run_rc = 1; + int j; + int pid1 = 0; + int pid2 = 0; + int create_swap= -1; + struct stat statbuf; +#ifndef INCLUDE_DINSTALL + const char * tty_commands[2] = { "bin/sh", "bin/sh"}; +#else + const char * tty_commands[2] = { "sbin/dinstall", "bin/sh"}; +#endif + char swap[20]; + int serial_console = 0; + + /* + * If I am started as /linuxrc instead of /sbin/init, I don't have the + * environment that init expects. I can't fix the signal behavior. Try + * to divorce from the controlling terminal with setsid(). This won't work + * if I am the process group leader. + */ + setsid(); + + signal(SIGUSR1, halt_signal); + signal(SIGUSR2, reboot_signal); + signal(SIGINT, reboot_signal); + signal(SIGTERM, exit_signal); + + reboot(RB_DISABLE_CAD); + + message(log, "%s: started. ", argv[0]); + + for ( j = 1; j < argc; j++ ) { + if ( strcmp(argv[j], "single") == 0 ) { + run_rc = 0; + tty_commands[0] = "bin/sh"; + tty_commands[1] = 0; + } + } + for ( j = 0; __environ[j] != 0; j++ ) { + if ( strncmp(__environ[j], "tty", 3) == 0 + && __environ[j][3] >= '1' + && __environ[j][3] <= '2' + && __environ[j][4] == '=' ) { + const char * s = &__environ[j][5]; + + if ( *s == 0 || strcmp(s, "off") == 0 ) + s = 0; + + tty_commands[__environ[j][3] - '1'] = s; + } + /* Should catch the syntax of Sparc kernel console setting. */ + /* The kernel does not recognize a serial console when getting*/ + /* console=/dev/ttySX !! */ + else if ( strcmp(__environ[j], "console=ttya") == 0 ) { + serial_console=1; + } + else if ( strcmp(__environ[j], "console=ttyb") == 0 ) { + serial_console=2; + } + /* standard console settings */ + else if ( strncmp(__environ[j], "console=", 8) == 0 ) { + first_terminal=&(__environ[j][8]); + } + else if ( strncmp(__environ[j], "TERM=", 5) == 0) { + term_ptr=__environ[j]; + } + } + configure_terminals( serial_console ); + + printf("mounting /proc ...\n"); + if (mount("/proc","/proc","proc",0,0)) { + perror("mounting /proc failed\n"); + } + printf("\tdone.\n"); + + set_free_pages(); + + if (mem_total() < 3500) { /* not enough memory for standard install */ + int retval; + retval= stat("/etc/swappartition",&statbuf); + if (retval) { + printf(" +You do not have enough RAM, hence you must boot using the Boot Disk +for Low Memory systems. + +Read the instructions in the install.html file. +"); + while (1) {;} + } else { /* everything OK */ + FILE *f; + + f=fopen("/etc/swappartition","r"); + fgets(swap,19,f); + fclose(f); + *(strstr(swap,"\n"))='\0'; + + if (swapon(swap,0)) { + perror("swapon failed\n"); + } else { + f=fopen("/etc/swaps","w"); + fprintf(f,"%s none swap rw 0 0",swap); + fclose(f); + create_swap = 0; + } + } + } + + /* + * Don't modify **argv directly, it would show up in the "ps" display. + * I don't want "init" to look like "rc". + */ + arguments[0] = rc; + for ( j = 1; j < argc; j++ ) { + arguments[j] = argv[j]; + } + arguments[j] = 0; + + if ( run_rc ) + waitfor(run(rc, arguments, console, 0)); + + if ( 0 == create_swap) { + if (unlink("/etc/swappartition")) { + perror("unlinking /etc/swappartition"); + } + } + + arguments[0] = "-sh"; + arguments[1] = 0; + for ( ; ; ) { + int wpid; + int status; + + if ( pid1 == 0 && tty_commands[0] ) { + /* Ask before starting a shell */ + /* + arguments[0] = tty_commands[0]; + */ + pid1 = run(tty_commands[0], arguments, first_terminal, 0); + } + if ( pid2 == 0 && tty_commands[1] ) + pid2 = run(tty_commands[1], arguments, second_terminal, 1); + wpid = wait(&status); + if ( wpid > 0 ) { + /* DEBUGGING */ + message(log, "pid %d exited, status=%x.\n", wpid, status); + } + if ( wpid == pid1 ) { + pid1 = 0; + } + if ( wpid == pid2 ) + pid2 = 0; + } +} + +void +configure_terminals( int serial_cons ) +{ + //struct stat statbuf; + char *tty; + + switch (serial_cons) { + case 1: + strcpy( console, "/dev/ttyS0" ); + break; + case 2: + strcpy( console, "/dev/ttyS1" ); + break; + default: + tty = ttyname(0); + if (tty) { + strcpy( console, tty ); + if (!strncmp( tty, "/dev/ttyS", 9 )) + serial_cons=1; + } + else + /* falls back to /dev/tty1 if an error occurs */ + strcpy( console, default_console ); + } + if (!first_terminal) + first_terminal = console; + if (serial_cons && !strncmp(term_ptr,"TERM=linux",10)) + term_ptr = "TERM=vt100"; +} diff --git a/init/halt.c b/init/halt.c new file mode 100644 index 000000000..7f3ccf966 --- /dev/null +++ b/init/halt.c @@ -0,0 +1,12 @@ +#include "internal.h" +#include + +const char halt_usage[] = "halt\n" +"\n\t" +"\thalt the system.\n"; + +extern int +halt_main(struct FileInfo * i, int argc, char * * argv) +{ + return kill(1, SIGUSR1); +} diff --git a/init/init.c b/init/init.c new file mode 100644 index 000000000..4771722b9 --- /dev/null +++ b/init/init.c @@ -0,0 +1,438 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char init_usage[] = "Used internally by the system."; +char console[16] = ""; +const char * default_console = "/dev/tty1"; +char * first_terminal = NULL; +const char * second_terminal = "/dev/tty2"; +const char log[] = "/dev/tty3"; +char * term_ptr = NULL; + +static void +message(const char * terminal, const char * pattern, ...) +{ + int fd; + FILE * con = 0; + va_list arguments; + + /* + * Open the console device each time a message is printed. If the user + * has switched consoles, the open will get the new console. If we kept + * the console open, we'd always print to the same one. + */ + if ( ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0) + || ((con = fdopen(fd, "w")) == NULL) ) + return; + + va_start(arguments, pattern); + vfprintf(con, pattern, arguments); + va_end(arguments); + fclose(con); +} + +static int +waitfor(int pid) +{ + int status; + int wpid; + + message(log, "Waiting for process %d.\n", pid); + while ( (wpid = wait(&status)) != pid ) { + if ( wpid > 0 ) { + message( + log + ,"pid %d exited, status=%x.\n" + ,wpid + ,status); + } + } + return wpid; +} + +static int +run( + const char * program +,const char * const * arguments +,const char * terminal +,int get_enter) +{ + static const char control_characters[] = { + '\003', + '\034', + '\177', + '\025', + '\004', + '\0', + '\1', + '\0', + '\021', + '\023', + '\032', + '\0', + '\022', + '\017', + '\027', + '\026', + '\0' + }; + + static char * environment[] = { + "HOME=/", + "PATH=/bin:/sbin:/usr/bin:/usr/sbin", + "SHELL=/bin/sh", + 0, + "USER=root", + 0 + }; + + static const char press_enter[] = + "Please press Enter to activate this console. "; + + int pid; + + environment[3]=term_ptr; + + pid = fork(); + if ( pid == 0 ) { + struct termios t; + const char * const * arg; + + close(0); + close(1); + close(2); + setsid(); + + open(terminal, O_RDWR); + dup(0); + dup(0); + tcsetpgrp(0, getpgrp()); + + tcgetattr(0, &t); + memcpy(t.c_cc, control_characters, sizeof(control_characters)); + t.c_line = 0; + t.c_iflag = ICRNL|IXON|IXOFF; + t.c_oflag = OPOST|ONLCR; + t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN; + tcsetattr(0, TCSANOW, &t); + + if ( get_enter ) { + /* + * Save memory by not exec-ing anything large (like a shell) + * before the user wants it. This is critical if swap is not + * enabled and the system has low memory. Generally this will + * be run on the second virtual console, and the first will + * be allowed to start a shell or the installation system. + */ + char c; + write(1, press_enter, sizeof(press_enter) - 1); + read(0, &c, 1); + } + + message(log, "Executing "); + arg = arguments; + while ( *arg != 0 ) + message(log, "%s ", *arg++); + message(log, "\n"); + + execve(program, (char * *)arguments, (char * *)environment); + message(log, "%s: could not execute: %s.\r\n", program, strerror(errno)); + exit(-1); + } + return pid; +} + +static int +mem_total() +{ + char s[80]; + char *p; + FILE *f; + const char pattern[]="MemTotal:"; + + f=fopen("/proc/meminfo","r"); + while (NULL != fgets(s,79,f)) { + p=strstr(s, pattern); + if (NULL != p) { + fclose(f); + return(atoi(p+strlen(pattern))); + } + } + return -1; +} + +static void +set_free_pages() +{ + char s[80]; + FILE *f; + + f=fopen("/proc/sys/vm/freepages","r"); + fgets(s,79,f); + if (atoi(s) < 32) { + fclose(f); + f=fopen("/proc/sys/vm/freepages","w"); + fprintf(f,"30\t40\t50\n"); + printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n"); + } + fclose(f); +} + +static void +shutdown_system(int do_reboot) +{ + static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0}; + + sync(); + /* Allow Ctrl-Alt-Del to reboot system. */ + reboot(RB_ENABLE_CAD); + + /* Send signals to every process _except_ pid 1 */ + message(console, "Sending SIGHUP to all processes.\r\n"); + kill(-1, SIGHUP); + sleep(2); + sync(); + message(console, "Sending SIGKILL to all processes.\r\n"); + kill(-1, SIGKILL); + sleep(1); + waitfor(run("/bin/umount", umount_args, console, 0)); + sync(); + bdflush(1, 0); + sync(); + reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM); + exit(0); +} + +static void +halt_signal(int sig) +{ + shutdown_system(0); +} + +static void +reboot_signal(int sig) +{ + shutdown_system(1); +} + +static void +exit_signal(int sig) +{ + + /* initrd doesn't work anyway */ + + shutdown_system(1); + + /* This is used on the initial ramdisk */ + + /* message(log, "Init exiting."); + exit(0); + */ +} + +void +configure_terminals( int serial_cons ); + +extern int +init_main(struct FileInfo * i, int argc, char * * argv) +{ + static const char * const rc = "etc/rc"; + const char * arguments[100]; + int run_rc = 1; + int j; + int pid1 = 0; + int pid2 = 0; + int create_swap= -1; + struct stat statbuf; +#ifndef INCLUDE_DINSTALL + const char * tty_commands[2] = { "bin/sh", "bin/sh"}; +#else + const char * tty_commands[2] = { "sbin/dinstall", "bin/sh"}; +#endif + char swap[20]; + int serial_console = 0; + + /* + * If I am started as /linuxrc instead of /sbin/init, I don't have the + * environment that init expects. I can't fix the signal behavior. Try + * to divorce from the controlling terminal with setsid(). This won't work + * if I am the process group leader. + */ + setsid(); + + signal(SIGUSR1, halt_signal); + signal(SIGUSR2, reboot_signal); + signal(SIGINT, reboot_signal); + signal(SIGTERM, exit_signal); + + reboot(RB_DISABLE_CAD); + + message(log, "%s: started. ", argv[0]); + + for ( j = 1; j < argc; j++ ) { + if ( strcmp(argv[j], "single") == 0 ) { + run_rc = 0; + tty_commands[0] = "bin/sh"; + tty_commands[1] = 0; + } + } + for ( j = 0; __environ[j] != 0; j++ ) { + if ( strncmp(__environ[j], "tty", 3) == 0 + && __environ[j][3] >= '1' + && __environ[j][3] <= '2' + && __environ[j][4] == '=' ) { + const char * s = &__environ[j][5]; + + if ( *s == 0 || strcmp(s, "off") == 0 ) + s = 0; + + tty_commands[__environ[j][3] - '1'] = s; + } + /* Should catch the syntax of Sparc kernel console setting. */ + /* The kernel does not recognize a serial console when getting*/ + /* console=/dev/ttySX !! */ + else if ( strcmp(__environ[j], "console=ttya") == 0 ) { + serial_console=1; + } + else if ( strcmp(__environ[j], "console=ttyb") == 0 ) { + serial_console=2; + } + /* standard console settings */ + else if ( strncmp(__environ[j], "console=", 8) == 0 ) { + first_terminal=&(__environ[j][8]); + } + else if ( strncmp(__environ[j], "TERM=", 5) == 0) { + term_ptr=__environ[j]; + } + } + configure_terminals( serial_console ); + + printf("mounting /proc ...\n"); + if (mount("/proc","/proc","proc",0,0)) { + perror("mounting /proc failed\n"); + } + printf("\tdone.\n"); + + set_free_pages(); + + if (mem_total() < 3500) { /* not enough memory for standard install */ + int retval; + retval= stat("/etc/swappartition",&statbuf); + if (retval) { + printf(" +You do not have enough RAM, hence you must boot using the Boot Disk +for Low Memory systems. + +Read the instructions in the install.html file. +"); + while (1) {;} + } else { /* everything OK */ + FILE *f; + + f=fopen("/etc/swappartition","r"); + fgets(swap,19,f); + fclose(f); + *(strstr(swap,"\n"))='\0'; + + if (swapon(swap,0)) { + perror("swapon failed\n"); + } else { + f=fopen("/etc/swaps","w"); + fprintf(f,"%s none swap rw 0 0",swap); + fclose(f); + create_swap = 0; + } + } + } + + /* + * Don't modify **argv directly, it would show up in the "ps" display. + * I don't want "init" to look like "rc". + */ + arguments[0] = rc; + for ( j = 1; j < argc; j++ ) { + arguments[j] = argv[j]; + } + arguments[j] = 0; + + if ( run_rc ) + waitfor(run(rc, arguments, console, 0)); + + if ( 0 == create_swap) { + if (unlink("/etc/swappartition")) { + perror("unlinking /etc/swappartition"); + } + } + + arguments[0] = "-sh"; + arguments[1] = 0; + for ( ; ; ) { + int wpid; + int status; + + if ( pid1 == 0 && tty_commands[0] ) { + /* Ask before starting a shell */ + /* + arguments[0] = tty_commands[0]; + */ + pid1 = run(tty_commands[0], arguments, first_terminal, 0); + } + if ( pid2 == 0 && tty_commands[1] ) + pid2 = run(tty_commands[1], arguments, second_terminal, 1); + wpid = wait(&status); + if ( wpid > 0 ) { + /* DEBUGGING */ + message(log, "pid %d exited, status=%x.\n", wpid, status); + } + if ( wpid == pid1 ) { + pid1 = 0; + } + if ( wpid == pid2 ) + pid2 = 0; + } +} + +void +configure_terminals( int serial_cons ) +{ + //struct stat statbuf; + char *tty; + + switch (serial_cons) { + case 1: + strcpy( console, "/dev/ttyS0" ); + break; + case 2: + strcpy( console, "/dev/ttyS1" ); + break; + default: + tty = ttyname(0); + if (tty) { + strcpy( console, tty ); + if (!strncmp( tty, "/dev/ttyS", 9 )) + serial_cons=1; + } + else + /* falls back to /dev/tty1 if an error occurs */ + strcpy( console, default_console ); + } + if (!first_terminal) + first_terminal = console; + if (serial_cons && !strncmp(term_ptr,"TERM=linux",10)) + term_ptr = "TERM=vt100"; +} diff --git a/init/reboot.c b/init/reboot.c new file mode 100644 index 000000000..0388fbce7 --- /dev/null +++ b/init/reboot.c @@ -0,0 +1,12 @@ +#include "internal.h" +#include + +const char reboot_usage[] = "reboot\n" +"\n\t" +"\treboot the system.\n"; + +extern int +reboot_main(struct FileInfo * i, int argc, char * * argv) +{ + return kill(1, SIGUSR2); +} diff --git a/internal.h b/internal.h new file mode 100644 index 000000000..e658d3b7d --- /dev/null +++ b/internal.h @@ -0,0 +1,189 @@ +#ifndef _INTERNAL_H_ +#define _INTERNAL_H_ + +#include "busybox.def.h" + +#include +#include +#include +#include + + +/* Some useful definitions */ +typedef int BOOL; +#define STDIN 0 +#define STDOUT 1 +#define FALSE ((BOOL) 0) +#define TRUE ((BOOL) 1) + +#define PATH_LEN 1024 +#define BUF_SIZE 8192 +#define EXPAND_ALLOC 1024 + +#define isBlank(ch) (((ch) == ' ') || ((ch) == '\t')) +#define isDecimal(ch) (((ch) >= '0') && ((ch) <= '9')) +#define isOctal(ch) (((ch) >= '0') && ((ch) <= '7')) +#define isWildCard(ch) (((ch) == '*') || ((ch) == '?') || ((ch) == '[')) + + + +struct FileInfo { + unsigned int complainInPostProcess:1; + unsigned int changeUserID:1; + unsigned int changeGroupID:1; + unsigned int changeMode:1; + unsigned int create:1; + unsigned int force:1; + unsigned int recursive:1; + unsigned int processDirectoriesAfterTheirContents; + unsigned int makeParentDirectories:1; + unsigned int didOperation:1; + unsigned int isSymbolicLink:1; + unsigned int makeSymbolicLink:1; + unsigned int dyadic:1; + const char* source; + const char* destination; + int directoryLength; + uid_t userID; + gid_t groupID; + mode_t andWithMode; + mode_t orWithMode; + struct stat stat; + const struct Applet * + applet; +}; + +struct Applet { + const char* name; + int (*main)(int argc, char** argv); +}; + +extern void name_and_error(const char*); +extern int is_a_directory(const char*); +extern char* join_paths(char *, const char *, const char *); + +extern int descend( + struct FileInfo *o + ,int (*function)(const struct FileInfo * i)); + +extern struct mntent * + findMountPoint(const char*, const char *); + +extern void usage(const char*); +extern int busybox_main(int argc, char** argv); +extern int block_device_main(int argc, char** argv); +extern int cat_more_main(int argc, char** argv); +extern int chgrp_main(int argc, char** argv); +extern int chmod_main(int argc, char** argv); +extern int chown_main(int argc, char** argv); +extern int chroot_main(int argc, char** argv); +extern int clear_main(int argc, char** argv); +extern int date_main(int argc, char** argv); +extern int dd_main(int argc, char** argv); +extern int df_main(int argc, char** argv); +extern int dmesg_main(int argc, char** argv); +extern int dyadic_main(int argc, char** argv); +extern int false_main(int argc, char** argv); +extern int fdisk_main(int argc, char** argv); +extern int find_main(int argc, char** argv); +extern int grep_main(int argc, char** argv); +extern int halt_main(int argc, char** argv); +extern int init_main(int argc, char** argv); +extern int kill_main(int argc, char** argv); +extern int length_main(int argc, char** argv); +extern int ln_main(int argc, char** argv); +extern int loadkmap_main(int argc, char** argv); +extern int losetup_main(int argc, char** argv); +extern int ls_main(int argc, char** argv); +extern int makedevs_main(int argc, char** argv); +extern int math_main(int argc, char** argv); +extern int mknod_main(int argc, char** argv); +extern int mkswap_main(int argc, char** argv); +extern int mnc_main(int argc, char** argv); +extern int monadic_main(int argc, char** argv); +extern int mount_main(int argc, char** argv); +extern int mt_main(int argc, char** argv); +extern int printf_main(int argc, char** argv); +extern int pwd_main(int argc, char** argv); +extern int reboot_main(int argc, char** argv); +extern int rm_main(int argc, char** argv); +extern int scan_partitions_main(int argc, char** argv); +extern int sh_main(int argc, char** argv); +extern int sleep_main(int argc, char** argv); +extern int tar_main(int argc, char** argv); +extern int sync_main(int argc, char** argv); +extern int tput_main(int argc, char** argv); +extern int true_main(int argc, char** argv); +extern int tryopen_main(int argc, char** argv); +extern int umount_main(int argc, char** argv); +extern int update_main(int argc, char** argv); +extern int zcat_main(int argc, char** argv); +extern int gzip_main(int argc, char** argv); + +extern int +parse_mode( + const char* s +,mode_t * or +,mode_t * and +,int * group_execute); + +extern int parse_user_name(const char* string, struct FileInfo * i); + +extern const char block_device_usage[]; +extern const char chgrp_usage[]; +extern const char chmod_usage[]; +extern const char chown_usage[]; +extern const char chroot_usage[]; +extern const char clear_usage[]; +extern const char cp_usage[]; +extern const char date_usage[]; +extern const char dd_usage[]; +extern const char df_usage[]; +extern const char dmesg_usage[]; +extern const char dutmp_usage[]; +extern const char false_usage[]; +extern const char fdflush_usage[]; +extern const char find_usage[]; +extern const char grep_usage[]; +extern const char halt_usage[]; +extern const char init_usage[]; +extern const char kill_usage[]; +extern const char length_usage[]; +extern const char ln_usage[]; +extern const char loadkmap_usage[]; +extern const char losetup_usage[]; +extern const char ls_usage[]; +extern const char math_usage[]; +extern const char makedevs_usage[]; +extern const char mkdir_usage[]; +extern const char mknod_usage[]; +extern const char mkswap_usage[]; +extern const char mnc_usage[]; +extern const char more_usage[]; +extern const char mount_usage[]; +extern const char mt_usage[]; +extern const char mv_usage[]; +extern const char printf_usage[]; +extern const char pwd_usage[]; +extern const char reboot_usage[]; +extern const char rm_usage[]; +extern const char rmdir_usage[]; +extern const char scan_partitions_usage[]; +extern const char sleep_usage[]; +extern const char tar_usage[]; +extern const char swapoff_usage[]; +extern const char swapon_usage[]; +extern const char sync_usage[]; +extern const char touch_usage[]; +extern const char tput_usage[]; +extern const char true_usage[]; +extern const char tryopen_usage[]; +extern const char umount_usage[]; +extern const char update_usage[]; +extern const char zcat_usage[]; +extern const char gzip_usage[]; + + + +#endif + diff --git a/kill.c b/kill.c new file mode 100644 index 000000000..da025fafc --- /dev/null +++ b/kill.c @@ -0,0 +1,140 @@ +#include "internal.h" +#include +#include +#include +#include + +const char kill_usage[] = "kill [-signal] process-id [process-id ...]\n"; + +struct signal_name { + const char * name; + int number; +}; + +const struct signal_name signames[] = { + { "HUP", SIGHUP }, + { "INT", SIGINT }, + { "QUIT", SIGQUIT }, + { "ILL", SIGILL }, + { "TRAP", SIGTRAP }, + { "ABRT", SIGABRT }, +#ifndef __alpha__ + { "IOT", SIGIOT }, +#endif +#if defined(sparc) || defined(__alpha__) + { "EMT", SIGEMT }, +#else + { "BUS", SIGBUS }, +#endif + { "FPE", SIGFPE }, + { "KILL", SIGKILL }, +#if defined(sparc) || defined(__alpha__) + { "BUS", SIGBUS }, +#else + { "USR1", SIGUSR1 }, +#endif + { "SEGV", SIGSEGV }, +#if defined(sparc) || defined(__alpha__) + { "SYS", SIGSYS }, +#else + { "USR2", SIGUSR2 }, +#endif + { "PIPE", SIGPIPE }, + { "ALRM", SIGALRM }, + { "TERM", SIGTERM }, +#if defined(sparc) || defined(__alpha__) + { "URG", SIGURG }, + { "STOP", SIGSTOP }, + { "TSTP", SIGTSTP }, + { "CONT", SIGCONT }, + { "CHLD", SIGCHLD }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, + { "IO", SIGIO }, +# ifndef __alpha__ + { "POLL", SIGIO }, +# endif + { "XCPU", SIGXCPU }, + { "XFSZ", SIGXFSZ }, + { "VTALRM", SIGVTALRM }, + { "PROF", SIGPROF }, + { "WINCH", SIGWINCH }, +# ifdef __alpha__ + { "INFO", SIGINFO }, +# else + { "LOST", SIGLOST }, +# endif + { "USR1", SIGUSR1 }, + { "USR2", SIGUSR2 }, +#else + { "STKFLT", SIGSTKFLT }, + { "CHLD", SIGCHLD }, + { "CONT", SIGCONT }, + { "STOP", SIGSTOP }, + { "TSTP", SIGTSTP }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, + { "URG", SIGURG }, + { "XCPU", SIGXCPU }, + { "XFSZ", SIGXFSZ }, + { "VTALRM", SIGVTALRM }, + { "PROF", SIGPROF }, + { "WINCH", SIGWINCH }, + { "IO", SIGIO }, + { "POLL", SIGPOLL }, + { "PWR", SIGPWR }, + { "UNUSED", SIGUNUSED }, +#endif + { 0, 0 } +}; + +extern int +kill_main(struct FileInfo * i, int argc, char * * argv) +{ + int had_error = 0; + int sig = SIGTERM; + if ( argv[1][0] == '-' ) { + if ( argv[1][1] >= '0' && argv[1][1] <= '9' ) { + sig = atoi(&argv[1][1]); + if ( sig < 0 || sig >= NSIG ) { + usage(kill_usage); + exit(-1); + } + } + else { + const struct signal_name * s = signames; + for ( ; ; ) { + if ( strcmp(s->name, &argv[1][1]) == 0 ) { + sig = s->number; + break; + } + s++; + if ( s->name == 0 ) { + usage(kill_usage); + exit(-1); + } + } + } + argv++; + argc--; + + } + while ( argc > 1 ) { + int pid; + if ( argv[1][0] < '0' || argv[1][0] > '9' ) { + usage(kill_usage); + exit(-1); + } + pid = atoi(argv[1]); + if ( kill(pid, sig) != 0 ) { + had_error = 1; + perror(argv[1]); + } + argv++; + argc--; + } + if ( had_error ) + return -1; + else + return 0; +} diff --git a/length.c b/length.c new file mode 100644 index 000000000..284bbfdf9 --- /dev/null +++ b/length.c @@ -0,0 +1,13 @@ +#include "internal.h" +#include +#include +#include + +const char length_usage[] = "length string"; + +int +length_main(struct FileInfo * i, int argc, char * * argv) +{ + printf("%d\n", strlen(argv[1])); + return 0; +} diff --git a/ln.c b/ln.c new file mode 100644 index 000000000..3e87b579e --- /dev/null +++ b/ln.c @@ -0,0 +1,52 @@ +#include "internal.h" +#include +#include +#include +#include + +const char ln_usage[] = "ln [-s] [-f] original-name additional-name\n" +"\n" +"\tAdd a new name that refers to the same file as \"original-name\"\n" +"\n" +"\t-s:\tUse a \"symbolic\" link, instead of a \"hard\" link.\n" +"\t-f:\tRemove existing destination files.\n"; + +int +ln_fn(const struct FileInfo * i) +{ + int status = 0; + char d[PATH_MAX]; + const char * destination = i->destination; + + if ( !i->makeSymbolicLink && (i->stat.st_mode & S_IFMT) == S_IFDIR ) { + fprintf(stderr, "Please use \"ln -s\" to link directories.\n"); + return 1; + } + + /* + * If the destination is a directory, create a file within it. + */ + if ( is_a_directory(i->destination) ) { + destination = join_paths( + d + ,i->destination + ,&i->source[i->directoryLength]); + } + + if ( i->force ) + status = ( unlink(destination) && errno != ENOENT ); + + if ( status == 0 ) { + if ( i->makeSymbolicLink ) + status = symlink(i->source, destination); + else + status = link(i->source, destination); + } + + if ( status != 0 ) { + name_and_error(destination); + return 1; + } + else + return 0; +} diff --git a/loadkmap.c b/loadkmap.c new file mode 100644 index 000000000..0f092d193 --- /dev/null +++ b/loadkmap.c @@ -0,0 +1,68 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include + + +const char loadkmap_usage[] = "loadkmap\n" +"\n" +"\tLoad a binary keyboard translation table from standard input.\n" +"\n"; + + +int +loadkmap_main(struct FileInfo * info, int argc, char * * argv) +{ + struct kbentry ke; + u_short *ibuff; + int i,j,fd,readsz,pos,ibuffsz=NR_KEYS * sizeof(u_short); + char flags[MAX_NR_KEYMAPS],magic[]="bkeymap",buff[7]; + + fd = open("/dev/tty0", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Error opening /dev/tty0: %s\n", strerror(errno)); + return 1; + } + + read(0,buff,7); + if (0 != strncmp(buff,magic,7)) { + fprintf(stderr, "This is not a valid binary keymap.\n"); + return 1; + } + + if ( MAX_NR_KEYMAPS != read(0,flags,MAX_NR_KEYMAPS) ) { + fprintf(stderr, "Error reading keymap flags: %s\n", strerror(errno)); + return 1; + } + + ibuff=(u_short *) malloc(ibuffsz); + if (!ibuff) { + fprintf(stderr, "Out of memory.\n"); + return 1; + } + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +/* #include "loop.h" */ + +/* + * include/linux/loop.h + * + * Written by Theodore Ts'o, 3/29/93. + * + * Copyright 1993 by Theodore Ts'o. Redistribution of this file is + * permitted under the GNU Public License. + */ + +#define LO_NAME_SIZE 64 +#define LO_KEY_SIZE 32 + +struct loop_info { + int lo_number; /* ioctl r/o */ + dev_t lo_device; /* ioctl r/o */ + unsigned long lo_inode; /* ioctl r/o */ + dev_t lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned long lo_init[2]; + char reserved[4]; +}; + +/* + * IOCTL commands --- we will commandeer 0x4C ('L') + */ + +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define LOOP_SET_STATUS 0x4C02 +#define LOOP_GET_STATUS 0x4C03 + +/* #include "lomount.h" */ + +extern int set_loop (const char *, const char *, int, int *); +extern int del_loop (const char *); + +static void show_loop(const char *device) +{ + struct loop_info loopinfo; + int fd; + + if ((fd = open(device, O_RDWR)) < 0) { + perror(device); + return; + } + if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) < 0) { + perror("Cannot get loop info"); + close(fd); + return; + } + printf("%s: [%04x]:%ld (%s) offset %d\n", + device, (unsigned int)loopinfo.lo_device, loopinfo.lo_inode, + loopinfo.lo_name, loopinfo.lo_offset); + close(fd); +} + + +int set_loop(const char *device, const char *file, int offset, int *loopro) +{ + struct loop_info loopinfo; + int fd, ffd, mode; + + mode = *loopro ? O_RDONLY : O_RDWR; + if ((ffd = open (file, mode)) < 0 && !*loopro + && (errno != EROFS || (ffd = open (file, mode = O_RDONLY)) < 0)) { + perror (file); + return 1; + } + if ((fd = open (device, mode)) < 0) { + close(ffd); + perror (device); + return 1; + } + *loopro = (mode == O_RDONLY); + + memset(&loopinfo, 0, sizeof(loopinfo)); + strncpy(loopinfo.lo_name, file, LO_NAME_SIZE); + loopinfo.lo_name[LO_NAME_SIZE-1] = 0; + + loopinfo.lo_offset = offset; + + loopinfo.lo_encrypt_key_size = 0; + if (ioctl(fd, LOOP_SET_FD, ffd) < 0) { + perror("ioctl: LOOP_SET_FD"); + exit(1); + } + if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) { + (void) ioctl(fd, LOOP_CLR_FD, 0); + perror("ioctl: LOOP_SET_STATUS"); + exit(1); + } + close(fd); + close(ffd); + return 0; +} + +int del_loop(const char *device) +{ + int fd; + + if ((fd = open(device, O_RDONLY)) < 0) { + perror(device); + exit(1); + } + if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { + perror("ioctl: LOOP_CLR_FD"); + exit(1); + } + close(fd); + return(0); +} + + +static int losetup_usage_fn(void) +{ + fprintf(stderr, losetup_usage); + exit(1); +} + +int losetup_main(struct FileInfo * i, int argc, char * * argv) +{ + char *offset; + int delete,off,c; + int ro = 0; + + delete = off = 0; + offset = NULL; + while ((c = getopt(argc,argv,"do:")) != EOF) { + switch (c) { + case 'd': + delete = 1; + break; + case 'o': + offset = optarg; + break; + default: + losetup_usage_fn(); + } + } + if (argc == 1) losetup_usage_fn(); + if ((delete && (argc != optind+1 || offset)) || + (!delete && (argc < optind+1 || argc > optind+2))) + losetup_usage_fn(); + if (argc == optind+1) + if (delete) + del_loop(argv[optind]); + else + show_loop(argv[optind]); + else { + if (offset && sscanf(offset,"%d",&off) != 1) + losetup_usage_fn(); + set_loop(argv[optind],argv[optind+1],off,&ro); + } + return 0; +} diff --git a/ls.c b/ls.c new file mode 100644 index 000000000..2566beea0 --- /dev/null +++ b/ls.c @@ -0,0 +1,542 @@ +#include "internal.h" +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * 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. + */ + +/* + * To achieve a small memory footprint, this version of 'ls' doesn't do any + * file sorting, and only has the most essential command line switches + * (i.e. the ones I couldn't live without :-) All features which involve + * linking in substantial chunks of libc can be disabled. + * + * Although I don't really want to add new features to this program to + * keep it small, I *am* interested to receive bug fixes and ways to make + * it more portable. + * + * KNOWN BUGS: + * 1. messy output if you mix files and directories on the command line + * 2. ls -l of a directory doesn't give "total " header + * 3. ls of a symlink to a directory doesn't list directory contents + * 4. hidden files can make column width too large + * NON-OPTIMAL BEHAVIOUR: + * 1. autowidth reads directories twice + * 2. if you do a short directory listing without filetype characters + * appended, there's no need to stat each one + * PORTABILITY: + * 1. requires lstat (BSD) - how do you do it without? + */ + +#define FEATURE_USERNAME /* show username/groupnames (libc6 uses NSS) */ +#define FEATURE_TIMESTAMPS /* show file timestamps */ +#define FEATURE_AUTOWIDTH /* calculate terminal & column widths */ +#define FEATURE_FILETYPECHAR /* enable -p and -F */ + +#undef OP_BUF_SIZE 1024 /* leave undefined for unbuffered output */ + +#define TERMINAL_WIDTH 80 /* use 79 if your terminal has linefold bug */ +#define COLUMN_WIDTH 14 /* default if AUTOWIDTH not defined */ +#define COLUMN_GAP 2 /* includes the file type char, if present */ + +/************************************************************************/ + +#define HAS_REWINDDIR + +#if 1 /* FIXME libc 6 */ +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#ifdef FEATURE_USERNAME +#include +#include +#endif +#ifdef FEATURE_TIMESTAMPS +#include +#endif + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#ifdef FEATURE_FILETYPECHAR +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +#endif + +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +#define MODE1 "rwxrwxrwx" +#define MODE0 "---------" +#define SMODE1 "..s..s..t" +#define SMODE0 "..S..S..T" + +/* The 9 mode bits to test */ + +static const umode_t MBIT[] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH +}; + +/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ + +static const umode_t SBIT[] = { + 0, 0, S_ISUID, + 0, 0, S_ISGID, + 0, 0, S_ISVTX +}; + +#define FMT_AUTO 0 +#define FMT_LONG 1 /* one record per line, extended info */ +#define FMT_SINGLE 2 /* one record per line */ +#define FMT_ROWS 3 /* print across rows */ +#define FMT_COLUMNS 3 /* fill columns (same, since we don't sort) */ + +#define TIME_MOD 0 +#define TIME_CHANGE 1 +#define TIME_ACCESS 2 + +#define DISP_FTYPE 1 /* show character for file type */ +#define DISP_EXEC 2 /* show '*' if regular executable file */ +#define DISP_HIDDEN 4 /* show files starting . (except . and ..) */ +#define DISP_DOT 8 /* show . and .. */ +#define DISP_NUMERIC 16 /* numeric uid and gid */ +#define DISP_FULLTIME 32 /* show extended time display */ +#define DIR_NOLIST 64 /* show directory as itself, not contents */ +#define DISP_DIRNAME 128 /* show directory name (for internal use) */ +#define DIR_RECURSE 256 /* -R (not yet implemented) */ + +static unsigned char display_fmt = FMT_AUTO; +static unsigned short opts = 0; +static unsigned short column = 0; + +#ifdef FEATURE_AUTOWIDTH +static unsigned short terminal_width = 0, column_width = 0; +#else +#define terminal_width TERMINAL_WIDTH +#define column_width COLUMN_WIDTH +#endif + +#ifdef FEATURE_TIMESTAMPS +static unsigned char time_fmt = TIME_MOD; +#endif + +#define wr(data,len) fwrite(data, 1, len, stdout) + +static void writenum(long val, short minwidth) +{ + char scratch[20]; + + char *p = scratch + sizeof(scratch); + short len = 0; + short neg = (val < 0); + + if (neg) val = -val; + do + *--p = (val % 10) + '0', len++, val /= 10; + while (val); + if (neg) + *--p = '-', len++; + while (len < minwidth) + *--p = ' ', len++; + wr(p, len); + column += len; +} + +static void newline(void) +{ + if (column > 0) { + wr("\n", 1); + column = 0; + } +} + +static void tab(short col) +{ + static const char spaces[] = " "; + #define nspaces ((sizeof spaces)-1) /* null terminator! */ + + short n = col - column; + + if (n > 0) { + column = col; + while (n > nspaces) { + wr(spaces, nspaces); + n -= nspaces; + } + /* must be 1...(sizeof spaces) left */ + wr(spaces, n); + } + #undef nspaces +} + +#ifdef FEATURE_FILETYPECHAR +static char append_char(umode_t mode) +{ + if (!(opts & DISP_FTYPE)) + return '\0'; + if ((opts & DISP_EXEC) && S_ISREG(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +/** + ** + ** Display a file or directory as a single item + ** (in either long or short format) + ** + **/ + +static void list_single(const char *name, struct stat *info) +{ + char scratch[20]; + short len = strlen(name); +#ifdef FEATURE_FILETYPECHAR + char append = append_char(info->st_mode); +#endif + + if (display_fmt == FMT_LONG) { + umode_t mode = info->st_mode; + int i; + + scratch[0] = TYPECHAR(mode); + for (i=0; i<9; i++) + if (mode & SBIT[i]) + scratch[i+1] = (mode & MBIT[i]) + ? SMODE1[i] + : SMODE0[i]; + else + scratch[i+1] = (mode & MBIT[i]) + ? MODE1[i] + : MODE0[i]; + newline(); + wr(scratch, 10); + column=10; + writenum((long)info->st_nlink,(short)4); + fputs(" ", stdout); +#ifdef FEATURE_USERNAME + if (!(opts & DISP_NUMERIC)) { + struct passwd *pw = getpwuid(info->st_uid); + if (pw) + fputs(pw->pw_name, stdout); + else + writenum((long)info->st_uid,(short)0); + } else +#endif + writenum((long)info->st_uid,(short)0); + tab(24); +#ifdef FEATURE_USERNAME + if (!(opts & DISP_NUMERIC)) { + struct group *gr = getgrgid(info->st_gid); + if (gr) + fputs(gr->gr_name, stdout); + else + writenum((long)info->st_gid,(short)0); + } else +#endif + writenum((long)info->st_gid,(short)0); + tab(33); + if (S_ISBLK(mode) || S_ISCHR(mode)) { + writenum((long)MAJOR(info->st_rdev),(short)3); + fputs(", ", stdout); + writenum((long)MINOR(info->st_rdev),(short)3); + } + else + writenum((long)info->st_size,(short)8); + fputs(" ", stdout); +#ifdef FEATURE_TIMESTAMPS + { + time_t cal; + char *string; + + switch(time_fmt) { + case TIME_CHANGE: + cal=info->st_ctime; break; + case TIME_ACCESS: + cal=info->st_atime; break; + default: + cal=info->st_mtime; break; + } + string=ctime(&cal); + if (opts & DISP_FULLTIME) + wr(string,24); + else { + time_t age = time(NULL) - cal; + wr(string+4,7); /* mmm_dd_ */ + if(age < 3600L*24*365/2 && age > -15*60) + /* hh:mm if less than 6 months old */ + wr(string+11,5); + else + /* _yyyy otherwise */ + wr(string+19,5); + } + wr(" ", 1); + } +#else + fputs("--- -- ----- ", stdout); +#endif + wr(name, len); + if (S_ISLNK(mode)) { + wr(" -> ", 4); + len = readlink(name, scratch, sizeof scratch); + if (len > 0) fwrite(scratch, 1, len, stdout); +#ifdef FEATURE_FILETYPECHAR + /* show type of destination */ + if (opts & DISP_FTYPE) { + if (!stat(name, info)) { + append = append_char(info->st_mode); + if (append) + fputc(append, stdout); + } + } +#endif + } +#ifdef FEATURE_FILETYPECHAR + else if (append) + wr(&append, 1); +#endif + } else { + static short nexttab = 0; + + /* sort out column alignment */ + if (column == 0) + ; /* nothing to do */ + else if (display_fmt == FMT_SINGLE) + newline(); + else { + if (nexttab + column_width > terminal_width +#ifndef FEATURE_AUTOWIDTH + || nexttab + len >= terminal_width +#endif + ) + newline(); + else + tab(nexttab); + } + /* work out where next column starts */ +#ifdef FEATURE_AUTOWIDTH + /* we know the calculated width is big enough */ + nexttab = column + column_width + COLUMN_GAP; +#else + /* might cover more than one fixed-width column */ + nexttab = column; + do + nexttab += column_width + COLUMN_GAP; + while (nexttab < (column + len + COLUMN_GAP)); +#endif + /* now write the data */ + wr(name, len); + column = column + len; +#ifdef FEATURE_FILETYPECHAR + if (append) + wr(&append, 1), column++; +#endif + } +} + +/** + ** + ** List the given file or directory, expanding a directory + ** to show its contents if required + ** + **/ + +static int list_item(const char *name) +{ + struct stat info; + DIR *dir; + struct dirent *entry; + char fullname[MAXNAMLEN+1], *fnend; + + if (lstat(name, &info)) + goto listerr; + + if (!S_ISDIR(info.st_mode) || + (opts & DIR_NOLIST)) { + list_single(name, &info); + return 0; + } + + /* Otherwise, it's a directory we want to list the contents of */ + + if (opts & DISP_DIRNAME) { /* identify the directory */ + if (column) + wr("\n\n", 2), column = 0; + wr(name, strlen(name)); + wr(":\n", 2); + } + + dir = opendir(name); + if (!dir) goto listerr; +#ifdef FEATURE_AUTOWIDTH + column_width = 0; + while ((entry = readdir(dir)) != NULL) { + short w = strlen(entry->d_name); + if (column_width < w) + column_width = w; + } +#ifdef HAS_REWINDDIR + rewinddir(dir); +#else + closedir(dir); + dir = opendir(name); + if (!dir) goto listerr; +#endif +#endif + + /* List the contents */ + + strcpy(fullname,name); /* *** ignore '.' by itself */ + fnend=fullname+strlen(fullname); + if (fnend[-1] != '/') + *fnend++ = '/'; + + while ((entry = readdir(dir)) != NULL) { + const char *en=entry->d_name; + if (en[0] == '.') { + if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ + if (!(opts & DISP_DOT)) + continue; + } + else if (!(opts & DISP_HIDDEN)) + continue; + } + /* FIXME: avoid stat if not required */ + strcpy(fnend, entry->d_name); + if (lstat(fullname, &info)) + goto direrr; /* (shouldn't fail) */ + list_single(entry->d_name, &info); + } + closedir(dir); + return 0; + +direrr: + closedir(dir); +listerr: + newline(); + name_and_error(name); + return 1; +} + +const char ls_usage[] = "Usage: ls [-1a" +#ifdef FEATURE_TIMESTAMPS + "c" +#endif + "d" +#ifdef FEATURE_TIMESTAMPS + "e" +#endif + "ln" +#ifdef FEATURE_FILETYPECHAR + "p" +#endif +#ifdef FEATURE_TIMESTAMPS + "u" +#endif + "xAC" +#ifdef FEATURE_FILETYPECHAR + "F" +#endif +#ifdef FEATURE_RECURSIVE + "R" +#endif + "] [filenames...]\n"; + +extern int +ls_main(struct FileInfo * not_used, int argc, char * * argv) +{ + int argi=1, i; + + /* process options */ + while (argi < argc && argv[argi][0] == '-') { + const char *p = &argv[argi][1]; + + if (!*p) goto print_usage_message; /* "-" by itself not allowed */ + if (*p == '-') { + if (!p[1]) { /* "--" forces end of options */ + argi++; + break; + } + /* it's a long option name - we don't support them */ + goto print_usage_message; + } + + while (*p) + switch (*p++) { + case 'l': display_fmt = FMT_LONG; break; + case '1': display_fmt = FMT_SINGLE; break; + case 'x': display_fmt = FMT_ROWS; break; + case 'C': display_fmt = FMT_COLUMNS; break; +#ifdef FEATURE_FILETYPECHAR + case 'p': opts |= DISP_FTYPE; break; + case 'F': opts |= DISP_FTYPE|DISP_EXEC; break; +#endif + case 'A': opts |= DISP_HIDDEN; break; + case 'a': opts |= DISP_HIDDEN|DISP_DOT; break; + case 'n': opts |= DISP_NUMERIC; break; + case 'd': opts |= DIR_NOLIST; break; +#ifdef FEATURE_RECURSIVE + case 'R': opts |= DIR_RECURSE; break; +#endif +#ifdef FEATURE_TIMESTAMPS + case 'u': time_fmt = TIME_ACCESS; break; + case 'c': time_fmt = TIME_CHANGE; break; + case 'e': opts |= DISP_FULLTIME; break; +#endif + default: goto print_usage_message; + } + + argi++; + } + + /* choose a display format */ + if (display_fmt == FMT_AUTO) + display_fmt = isatty(STDOUT_FILENO) ? FMT_COLUMNS : FMT_SINGLE; + if (argi < argc - 1) + opts |= DISP_DIRNAME; /* 2 or more items? label directories */ +#ifdef FEATURE_AUTOWIDTH + /* could add a -w option and/or TIOCGWINSZ call */ + if (terminal_width < 1) terminal_width = TERMINAL_WIDTH; + + for (i = argi; i < argc; i++) { + int len = strlen(argv[i]); + if (column_width < len) + column_width = len; + } +#endif + + /* process files specified, or current directory if none */ + i=0; + if (argi == argc) + i = list_item("."); + while (argi < argc) + i |= list_item(argv[argi++]); + newline(); + return i; + +print_usage_message: + usage(ls_usage); + return 1; +} diff --git a/makedevs.c b/makedevs.c new file mode 100644 index 000000000..691236e29 --- /dev/null +++ b/makedevs.c @@ -0,0 +1,95 @@ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * makedevs + * Make ranges of device files quickly. + * known bugs: can't deal with alpha ranges + */ + +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + +const char makedevs_usage[] = +"makedevs 0.01 -- Create an entire range of device files\n\n" +"\tmakedevs /dev/ttyS c 4 64 0 63 (ttyS0-ttyS63)\n" +"\tmakedevs /dev/hda b 3 0 0 8 s (hda,hda1-hda8)\n"; + +int +makedevs_main(struct FileInfo * i, int argc, char * * argv) +{ + +const char *basedev = argv[1]; +const char *type = argv[2]; +int major = atoi(argv[3]); +int Sminor = atoi(argv[4]); +int S = atoi(argv[5]); +int E = atoi(argv[6]); +int sbase = argc == 8 ? 1 : 0; + +mode_t mode = 0; +dev_t dev = 0; +char devname[255]; +char buf[255]; + + switch (type[0]) { + case 'c': + mode = S_IFCHR; break; + case 'b': + mode = S_IFBLK; break; + case 'f': + mode = S_IFIFO; break; + default: + usage(makedevs_usage); + return 2; + } + mode |= 0660; + + while ( S <= E ) { + + if (type[0] != 'f') + dev = (major << 8) | Sminor; + strcpy(devname, basedev); + + if (sbase == 0) { + sprintf(buf, "%d", S); + strcat(devname, buf); + } else { + sbase = 0; + } + + if (mknod (devname, mode, dev)) + printf("Failed to create: %s\n", devname); + + S++; Sminor++; + } + +return 0; +} + +/* +And this is what this program replaces. The shell is too slow! + +makedev () { +local basedev=$1; local S=$2; local E=$3 +local major=$4; local Sminor=$5; local type=$6 +local sbase=$7 + + if [ ! "$sbase" = "" ]; then + mknod "$basedev" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + fi + + while [ $S -le $E ]; do + mknod "$basedev$S" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + done +} +*/ diff --git a/math.c b/math.c new file mode 100644 index 000000000..5c1560a4e --- /dev/null +++ b/math.c @@ -0,0 +1,149 @@ +#include "internal.h" +#include +#include +#include +#include + +/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */ + +const char math_usage[] = "math expression ..."; + +static double stack[100]; +static unsigned int pointer; + +static void +push(double a) +{ + if ( pointer >= (sizeof(stack) / sizeof(*stack)) ) { + fprintf(stderr, "math: stack overflow\n"); + exit(-1); + } + else + stack[pointer++] = a; +} + +static double +pop() +{ + if ( pointer == 0 ) { + fprintf(stderr, "math: stack underflow\n"); + exit(-1); + } + return stack[--pointer]; +} + +static void +add() +{ + push(pop() + pop()); +} + +static void +sub() +{ + double subtrahend = pop(); + + push(pop() - subtrahend); +} + +static void +mul() +{ + push(pop() * pop()); +} + +static void +divide() +{ + double divisor = pop(); + push(pop() / divisor); +} + +static void +and() +{ + push((unsigned int)pop() & (unsigned int)pop()); +} + +static void +or() +{ + push((unsigned int)pop() | (unsigned int)pop()); +} + +static void +eor() +{ + push((unsigned int)pop() ^ (unsigned int)pop()); +} + +static void +not() +{ + push(~(unsigned int)pop()); +} + +static void +print() +{ + printf("%g\n", pop()); +} + +struct op { + const char * name; + void (*function)(); +}; + +static const struct op operators[] = { + { "add", add }, + { "and", and }, + { "div", divide }, + { "eor", eor }, + { "mul", mul }, + { "not", not }, + { "or", or }, + { "sub", sub }, + { 0, 0 } +}; + +static void +stack_machine(const char * argument) +{ + char * endPointer = 0; + double d; + const struct op * o = operators; + + if ( argument == 0 ) { + print(); + return; + } + + d = strtod(argument, &endPointer); + + if ( endPointer != argument ) { + push(d); + return; + } + + while ( o->name != 0 ) { + if ( strcmp(o->name, argument) == 0 ) { + (*(o->function))(); + return; + } + o++; + } + fprintf(stderr, "math: %s: syntax error.\n", argument); + exit(-1); +} + +int +math_main(struct FileInfo * i, int argc, char * * argv) +{ + while ( argc >= 2 ) { + stack_machine(argv[1]); + argv++; + argc--; + } + stack_machine(0); + return 0; +} diff --git a/miscutils/dutmp.c b/miscutils/dutmp.c new file mode 100644 index 000000000..e92b6700f --- /dev/null +++ b/miscutils/dutmp.c @@ -0,0 +1,47 @@ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * dutmp + * Takes utmp formated file on stdin and dumps it's contents + * out in colon delimited fields. Easy to 'cut' for shell based + * versions of 'who', 'last', etc. IP Addr is output in hex, + * little endian on x86. + * + * made against libc6 + */ + +#include "internal.h" +#include +#include + +const char dutmp_usage[] = "dutmp\n" +"\n" +"\tDump file or stdin utmp file format to stdout, pipe delimited.\n" +"\tdutmp /var/run/utmp\n"; + +extern int +dutmp_fn(const struct FileInfo * i) +{ + +FILE * f = stdin; +struct utmp * ut = (struct utmp *) malloc(sizeof(struct utmp) ); + + if ( i ) + if (! (f = fopen(i->source, "r"))) { + name_and_error(i->source); + return 1; + } + + while (fread (ut, 1, sizeof(struct utmp), f)) { + //printf("%d:%d:%s:%s:%s:%s:%d:%d:%ld:%ld:%ld:%x\n", + printf("%d|%d|%s|%s|%s|%s|%d|%d|%ld|%ld|%ld|%x\n", + ut->ut_type, ut->ut_pid, ut->ut_line, + ut->ut_id, ut->ut_user, ut->ut_host, + ut->ut_exit.e_termination, ut->ut_exit.e_exit, + ut->ut_session, + ut->ut_tv.tv_sec, ut->ut_tv.tv_usec, + ut->ut_addr); + } + +return 0; +} diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c new file mode 100644 index 000000000..691236e29 --- /dev/null +++ b/miscutils/makedevs.c @@ -0,0 +1,95 @@ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * makedevs + * Make ranges of device files quickly. + * known bugs: can't deal with alpha ranges + */ + +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + +const char makedevs_usage[] = +"makedevs 0.01 -- Create an entire range of device files\n\n" +"\tmakedevs /dev/ttyS c 4 64 0 63 (ttyS0-ttyS63)\n" +"\tmakedevs /dev/hda b 3 0 0 8 s (hda,hda1-hda8)\n"; + +int +makedevs_main(struct FileInfo * i, int argc, char * * argv) +{ + +const char *basedev = argv[1]; +const char *type = argv[2]; +int major = atoi(argv[3]); +int Sminor = atoi(argv[4]); +int S = atoi(argv[5]); +int E = atoi(argv[6]); +int sbase = argc == 8 ? 1 : 0; + +mode_t mode = 0; +dev_t dev = 0; +char devname[255]; +char buf[255]; + + switch (type[0]) { + case 'c': + mode = S_IFCHR; break; + case 'b': + mode = S_IFBLK; break; + case 'f': + mode = S_IFIFO; break; + default: + usage(makedevs_usage); + return 2; + } + mode |= 0660; + + while ( S <= E ) { + + if (type[0] != 'f') + dev = (major << 8) | Sminor; + strcpy(devname, basedev); + + if (sbase == 0) { + sprintf(buf, "%d", S); + strcat(devname, buf); + } else { + sbase = 0; + } + + if (mknod (devname, mode, dev)) + printf("Failed to create: %s\n", devname); + + S++; Sminor++; + } + +return 0; +} + +/* +And this is what this program replaces. The shell is too slow! + +makedev () { +local basedev=$1; local S=$2; local E=$3 +local major=$4; local Sminor=$5; local type=$6 +local sbase=$7 + + if [ ! "$sbase" = "" ]; then + mknod "$basedev" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + fi + + while [ $S -le $E ]; do + mknod "$basedev$S" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + done +} +*/ diff --git a/miscutils/mt.c b/miscutils/mt.c new file mode 100644 index 000000000..7d75fbd3d --- /dev/null +++ b/miscutils/mt.c @@ -0,0 +1,98 @@ +#include "internal.h" +#include +#include +#include + +const char mt_usage[] = "mt [-f device] opcode value\n"; + +struct mt_opcodes { + char * name; + short value; +}; + +/* missing: eod/seod, stoptions, stwrthreshold, densities */ +static const struct mt_opcodes opcodes[] = { + { "bsf", MTBSF }, + { "bsfm", MTBSFM }, + { "bsr", MTBSR }, + { "bss", MTBSS }, + { "datacompression", MTCOMPRESSION }, + { "eom", MTEOM }, + { "erase", MTERASE }, + { "fsf", MTFSF }, + { "fsfm", MTFSFM }, + { "fsr", MTFSR }, + { "fss", MTFSS }, + { "load", MTLOAD }, + { "lock", MTLOCK }, + { "mkpart", MTMKPART }, + { "nop", MTNOP }, + { "offline",MTOFFL }, + { "rewoffline",MTOFFL }, + { "ras1", MTRAS1 }, + { "ras2", MTRAS2 }, + { "ras3", MTRAS3 }, + { "reset", MTRESET }, + { "retension", MTRETEN }, + { "rew", MTREW }, + { "seek", MTSEEK }, + { "setblk", MTSETBLK }, + { "setdensity", MTSETDENSITY }, + { "drvbuffer", MTSETDRVBUFFER }, + { "setpart", MTSETPART }, + { "tell", MTTELL }, + { "wset", MTWSM }, + { "unload", MTUNLOAD }, + { "unlock", MTUNLOCK }, + { "eof", MTWEOF }, + { "weof", MTWEOF }, + { 0, 0 } +}; + +extern int +mt_main(struct FileInfo * i, int argc, char * * argv) +{ + const char * file = "/dev/tape"; + const struct mt_opcodes * code = opcodes; + struct mtop op; + int fd; + + if ( strcmp(argv[1], "-f") == 0 ) { + if ( argc < 4 ) { + usage(mt_usage); + return 1; + } + file = argv[2]; + argv += 2; + argc -= 2; + } + + while ( code->name != 0 ) { + if ( strcmp(code->name, argv[1]) == 0 ) + break; + code++; + } + + if ( code->name == 0 ) { + fprintf(stderr, "mt: unrecognized opcode %s.\n", argv[1]); + return 1; + } + + op.mt_op = code->value; + if ( argc >= 3 ) + op.mt_count = atoi(argv[2]); + else + op.mt_count = 1; /* One, not zero, right? */ + + if ( (fd = open(file, O_RDONLY, 0)) < 0 ) { + name_and_error(file); + return 1; + } + + if ( ioctl(fd, MTIOCTOP, &op) != 0 ) { + name_and_error(file); + return 1; + } + + return 0; +} diff --git a/miscutils/update.c b/miscutils/update.c new file mode 100644 index 000000000..f3b7fc0c8 --- /dev/null +++ b/miscutils/update.c @@ -0,0 +1,48 @@ +#include "internal.h" +#include + +const char update_usage[] = "update\n" +"\n" +"\tFlush buffered data to the disk devices every 30 seconds.\n"; + +#if defined(__GLIBC__) +#include +#else +_syscall2(int, bdflush, int, func, int, data); +#endif /* __GLIBC__ */ + +extern int +update_main(struct FileInfo * i, int argc, char * * argv) +{ + /* + * Update is actually two daemons, bdflush and update. + */ + int pid; + + pid = fork(); + if ( pid < 0 ) + return pid; + else if ( pid == 0 ) { + /* + * This is no longer necessary since 1.3.5x, but it will harmlessly + * exit if that is the case. + */ + strcpy(argv[0], "bdflush (update)"); + argv[1] = 0; + argv[2] = 0; + bdflush(1, 0); + _exit(0); + } + pid = fork(); + if ( pid < 0 ) + return pid; + else if ( pid == 0 ) { + argv[0] = "update"; + for ( ; ; ) { + sync(); + sleep(30); + } + } + + return 0; +} diff --git a/mkdir.c b/mkdir.c new file mode 100644 index 000000000..8f1fa04db --- /dev/null +++ b/mkdir.c @@ -0,0 +1,58 @@ +#include "internal.h" +#include +#include + +const char mkdir_usage[] = "mkdir [-m mode] directory [directory ...]\n" +"\tCreate directories.\n" +"\n" +"\t-m mode:\tSpecifiy the mode for the new directory\n" +"\t\tunder the argument directory."; + +/*make directories skipping the last part of the path. Used here and by untar*/ +int mkdir_until(const char *fpath, const struct FileInfo * fi) +{ + char path[PATH_MAX]; + char * s = path; + + strcpy(path, fpath); + if ( s[0] == '\0' && s[1] == '\0' ) { + usage(mkdir_usage); + return 1; + } + s++; + while ( *s != '\0' ) { + if ( *s == '/' ) { + int status; + + *s = '\0'; + status = mkdir(path, (fi?fi->orWithMode:0700) ); + *s = '/'; + + if ( status != 0 ) { + if ( errno != EEXIST ) { + name_and_error(fpath); + return 1; + } + } + + } + s++; + } + return 0; +} + +int +mkdir_fn(const struct FileInfo * i) +{ + if ( i->makeParentDirectories ) { + if(mkdir_until(i->source, i)) return 1; + } + + if ( mkdir(i->source, i->orWithMode) != 0 && errno != EEXIST ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} + diff --git a/mknod.c b/mknod.c new file mode 100644 index 000000000..b18394bec --- /dev/null +++ b/mknod.c @@ -0,0 +1,52 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char mknod_usage[] = "mknod file b|c|u|p major minor\n" +"\tMake special files.\n" +"\n" +"\tb:\tMake a block (buffered) device.\n" +"\tc or u:\tMake a character (un-buffered) device.\n" +"\tp:\tMake a named pipe. Major and minor are ignored for named pipes.\n"; + +int +mknod_main(struct FileInfo * i, int argc, char * * argv) +{ + mode_t mode = 0; + dev_t dev = 0; + + switch(argv[2][0]) { + case 'c': + case 'u': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'p': + mode = S_IFIFO; + break; + default: + usage(mknod_usage); + return 1; + } + + if ( mode == S_IFCHR || mode == S_IFBLK ) { + dev = (atoi(argv[3]) << 8) | atoi(argv[4]); + if ( argc != 5 ) { + usage(mknod_usage); + return 1; + } + } + + mode |= 0666; + + if ( mknod(argv[1], mode, dev) != 0 ) { + name_and_error(argv[1]); + return 1; + } + return 0; +} diff --git a/mkswap.c b/mkswap.c new file mode 100644 index 000000000..f797d1395 --- /dev/null +++ b/mkswap.c @@ -0,0 +1,253 @@ +#include "internal.h" +/* + * mkswap.c - set up a linux swap device + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * 20.12.91 - time began. Got VM working yesterday by doing this by hand. + * + * Usage: mkswap [-c] device [size-in-blocks] + * + * -c for readablility checking (use it unless you are SURE!) + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the + * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef __linux__ +# define volatile +#endif + +#define TEST_BUFFER_PAGES 8 + +const char mkswap_usage[] = "mkswap [-c] partition [block-count]\n" +"\n" +"\tPrepare a disk partition to be used as a swap partition.\n" +"\tThe default block count is the size of the entire partition.\n" +"\n" +"\t-c:\tCheck for read-ability.\n" +"\tblock-count\tUse only this many blocks.\n"; + +static const char * program_name = "mkswap"; +static const char * device_name = NULL; +static int DEV = -1; +static long PAGES = 0; +static int do_check = 0; +static int badpages = 0; + + +static long bit_test_and_set (unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + *addr = r | m; + return (r & m) != 0; +} + +static int bit_test_and_clear (unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + *addr = r & ~m; + return (r & m) != 0; +} + +/* + * Volatile to let gcc know that this doesn't return. When trying + * to compile this under minix, volatile gives a warning, as + * exit() isn't defined as volatile under minix. + */ +volatile void fatal_error(const char * fmt_string) +{ + fprintf(stderr,fmt_string,program_name,device_name); + exit(1); +} + +#define die(str) fatal_error("%s: " str "\n") + +static void check_blocks(int * signature_page) +{ + unsigned int current_page; + int do_seek = 1; + char buffer[PAGE_SIZE]; + + current_page = 0; + while (current_page < PAGES) { + if (!do_check) { + bit_test_and_set(signature_page,current_page++); + continue; + } else { + printf("\r%d", current_page); + } + if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != + current_page*PAGE_SIZE) + die("seek failed in check_blocks"); + if ( (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) ) { + bit_test_and_clear(signature_page,current_page++); + badpages++; + continue; + } + bit_test_and_set(signature_page,current_page++); + } + if (do_check) + printf("\n"); + if (badpages) + printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); +} + +static long valid_offset (int fd, int offset) +{ + char ch; + + if (lseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +static int count_blocks (int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const int mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + return (low + 1); +} + +static int get_size(const char *file) +{ + int fd; + int size; + + fd = open(file, O_RDWR); + if (fd < 0) { + perror(file); + exit(1); + } + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + close(fd); + return (size * 512); + } + + size = count_blocks(fd); + close(fd); + return size; +} + +int +mkswap(char *device_name, int pages, int check) + { + struct stat statbuf; + int goodpages; + int signature_page[PAGE_SIZE/sizeof(int)]; + + PAGES = pages; + do_check = check; + + memset(signature_page,0,PAGE_SIZE); + + if (device_name && !PAGES) { + PAGES = get_size(device_name) / PAGE_SIZE; + } + if (!device_name || PAGES<10) { + fprintf(stderr, + "%s: error: swap area needs to be at least %ldkB\n", + program_name, 10 * PAGE_SIZE / 1024); + /* usage(mkswap_usage); */ + exit(1); + } + if (PAGES > 8 * (PAGE_SIZE - 10)) { + PAGES = 8 * (PAGE_SIZE - 10); + fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n", + program_name, PAGES * PAGE_SIZE / 1024); + } + DEV = open(device_name,O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) { + perror(device_name); + exit(1); + } + if (!S_ISBLK(statbuf.st_mode)) + do_check=0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + die("Will not try to make swapdevice on '%s'"); + check_blocks(signature_page); + if (!bit_test_and_clear(signature_page,0)) + die("fatal: first page unreadable"); + goodpages = PAGES - badpages - 1; + if (goodpages <= 0) + die("Unable to set up swap-space: unreadable"); + printf("Setting up swapspace, size = %ld bytes\n",goodpages*PAGE_SIZE); + strncpy((char*)signature_page+PAGE_SIZE-10,"SWAP-SPACE",10); + if (lseek(DEV, 0, SEEK_SET)) + die("unable to rewind swap-device"); + if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE)) + die("unable to write signature page"); + + close(DEV); + return 0; +} + +int mkswap_main(struct FileInfo * unnecessary, int argc, char ** argv) +{ + char * tmp; + long int pages=0; + int check=0; + + if (argc && *argv) + program_name = *argv; + while (argc > 1) { + argv++; + argc--; + if (argv[0][0] != '-') + if (device_name) { + pages = strtol(argv[0],&tmp,0)>>(PAGE_SHIFT-10); + if (*tmp) { + usage(mkswap_usage); + exit(1); + } + } else + device_name = argv[0]; + else while (*++argv[0]) + switch (argv[0][0]) { + case 'c': check=1; break; + default: usage(mkswap_usage); + exit(1); + } + } + return mkswap(device_name, pages, check); +} diff --git a/mnc.c b/mnc.c new file mode 100644 index 000000000..9d59977da --- /dev/null +++ b/mnc.c @@ -0,0 +1,148 @@ +/* mnc: mini-netcat - built from the ground up for LRP + Copyright (C) 1998 Charles P. Wright + + 0.0.1 6K It works. + 0.0.2 5K Smaller and you can also check the exit condition if you wish. + + + 19980918 Busy Boxed! Dave Cinege + + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#include "internal.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +const char mnc_usage[] = +"mini-netcat 0.0.1 -- Open pipe to IP:port\n" +"\tmnc [IP] [port]\n"; + +int +mnc_main(struct FileInfo * i, int argc, char **argv) +{ + + int sfd; + int result; + int len; + int pid; + char ch; + + struct sockaddr_in address; + struct hostent *hostinfo; + +#ifdef SELECT + fd_set readfds, testfds; +#endif + + sfd = socket(AF_INET, SOCK_STREAM, 0); + + hostinfo = (struct hostent *) gethostbyname(argv[1]); + + if (!hostinfo) + { + exit(1); + } + + address.sin_family = AF_INET; + address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; + address.sin_port = htons(atoi(argv[2])); + + len = sizeof(address); + + result = connect(sfd, (struct sockaddr *)&address, len); + + if (result < 0) + { + exit(2); + } + +#ifdef SELECT + FD_ZERO(&readfds); + FD_SET(sfd, &readfds); + FD_SET(fileno(stdin), &readfds); + + while(1) + { + int fd; + int nread; + + testfds = readfds; + + result = select(FD_SETSIZE, &testfds, (fd_set *) NULL, (fd_set *) NULL, (struct timeval *) 0); + + if(result < 1) + { + exit(3); + } + + for(fd = 0; fd < FD_SETSIZE; fd++) + { + if(FD_ISSET(fd,&testfds)) + { + ioctl(fd, FIONREAD, &nread); + + if (nread == 0) + exit(0); + + if(fd == sfd) + { + read(sfd, &ch, 1); + write(fileno(stdout), &ch, 1); + } + else + { + read(fileno(stdin), &ch, 1); + write(sfd, &ch, 1); + } + } + } + } +#else + pid = fork(); + + if (!pid) + { + int retval; + retval = 1; + while(retval == 1) + { + retval = read(fileno(stdin), &ch, 1); + write(sfd, &ch, 1); + } + } + else + { + int retval; + retval = 1; + while(retval == 1) + { + retval = read(sfd, &ch, 1); + write(fileno(stdout), &ch, 1); + } + } + + exit(0); +#endif +} diff --git a/monadic.c b/monadic.c new file mode 100644 index 000000000..3c7fac25b --- /dev/null +++ b/monadic.c @@ -0,0 +1,126 @@ +#include "internal.h" +#include +#include +#include + +extern int +monadic_main( + struct FileInfo * i +,int argc +,char * * argv) +{ + int status = 0; + + while ( argc > 1 && argv[1][0] == '-' ) { + switch ( argv[1][1] ) { + case 'c': + i->create = 1; + break; + case 'f': + i->force = 1; + break; + case 'g': + if ( argc > 2 ) { + struct group * g; + if ( (g = getgrnam(argv[2])) == 0 ) { + fprintf(stderr, "%s: no such group.\n", argv[1]); + return 1; + } + i->groupID = g->gr_gid; + i->changeGroupID = 1; + i->complainInPostProcess = 1; + argc--; + argv++; + break; + } + usage(i->applet->usage); + return 1; + case 'm': + if ( argc > 2 ) { + status = parse_mode( + argv[2] + ,&i->orWithMode + ,&i->andWithMode, 0); + + if ( status == 0 ) { + i->changeMode = 1; + i->complainInPostProcess = 1; + argc--; + argv++; + break; + } + } + usage(i->applet->usage); + return 1; + case 'o': + if ( argc > 2 ) { + status = parse_user_name(argv[2], i); + if ( status != 0 ) + return status; + + i->changeUserID = 1; + i->complainInPostProcess = 1; + argc--; + argv++; + break; + } + usage(i->applet->usage); + return 1; + case 'p': + i->makeParentDirectories = 1; + break; + case 'r': + case 'R': + i->recursive = 1; + break; + case 's': + i->makeSymbolicLink = 1; + break; + default: + usage(i->applet->usage); + return 1; + } + argv++; + argc--; + } + while ( argc > 1 ) { + char * slash; + i->source = argv[1]; + if ( (slash = strrchr(i->source, '/')) != 0 ) { + i->directoryLength = slash - i->source; + if ( i->source[i->directoryLength] == '\0' ) + i->directoryLength = 0; + } + else + i->directoryLength = 0; + if ( !i->dyadic ) + i->destination = i->source; + + if ( lstat(i->source, &i->stat) == 0 ) { + i->isSymbolicLink = (i->stat.st_mode & S_IFMT)==S_IFLNK; + if ( i->isSymbolicLink ) + if ( stat(i->source, &i->stat) != 0 ) + memset(&i->stat, 0, sizeof(i->stat)); + } + else + memset(&i->stat, 0, sizeof(i->stat)); + + if ( i->isSymbolicLink + || !i->recursive + || ((i->stat.st_mode & S_IFMT) != S_IFDIR) ) { + + if ( i->applet->function ) + status = i->applet->function(i); + if ( status == 0 ) + status = post_process(i); + } + else + status = descend(i, i->applet->function); + + if ( status != 0 && !i->force ) + return status; + argv++; + argc--; + } + return 0; +} diff --git a/more.c b/more.c new file mode 100644 index 000000000..65409999b --- /dev/null +++ b/more.c @@ -0,0 +1,110 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +#define BB_MORE_TERM + +#ifdef BB_MORE_TERM + #include + #include + + FILE *cin; + struct termios initial_settings, new_settings; + + void gotsig(int sig) { + tcsetattr(fileno(cin), TCSANOW, &initial_settings); + exit(0); + } +#endif + +const char more_usage[] = "more [file]\n" +"\n" +"\tDisplays a file, one page at a time.\n" +"\tIf there are no arguments, the standard input is displayed.\n"; + +extern int +more_fn(const struct FileInfo * i) +{ + FILE * f = stdin; + int c; + int lines = 0, tlines = 0; + int next_page = 0; + int rows = 24, cols = 79; +#ifdef BB_MORE_TERM + long sizeb = 0; + struct stat st; + struct winsize win; +#endif + + if ( i ) { + if (! (f = fopen(i->source, "r") )) { + name_and_error(i->source); + return 1; + } + fstat(fileno(f), &st); + sizeb = st.st_size / 100; + } + +#ifdef BB_MORE_TERM + cin = fopen("/dev/tty", "r"); + tcgetattr(fileno(cin),&initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; + tcsetattr(fileno(cin), TCSANOW, &new_settings); + + (void) signal(SIGINT, gotsig); + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &win); + if (win.ws_row > 4) rows = win.ws_row - 2; + if (win.ws_col > 0) cols = win.ws_col - 1; + + +#endif + + while ( (c = getc(f)) != EOF ) { + if ( next_page ) { + char garbage; + int len; + tlines += lines; + lines = 0; + next_page = 0; //Percentage is based on bytes, not lines. + if ( i && i->source ) //It is not very acurate, but still useful. + len = printf("%s - %%%2ld - line: %d", i->source, (ftell(f) - sizeb - sizeb) / sizeb, tlines); + else + len = printf("line: %d", tlines); + + fflush(stdout); +#ifndef BB_MORE_TERM + read(2, &garbage, 1); +#else + do { + fread(&garbage, 1, 1, cin); + } while ((garbage != ' ') && (garbage != '\n')); + + if (garbage == '\n') { + lines = rows; + tlines -= rows; + } + garbage = 0; + //clear line, since tabs don't overwrite. + while(len-- > 0) putchar('\b'); + while(len++ < cols) putchar(' '); + while(len-- > 0) putchar('\b'); + fflush(stdout); +#endif + } + putchar(c); + if ( c == '\n' && ++lines == (rows + 1) ) + next_page = 1; + } + if ( f != stdin ) + fclose(f); +#ifdef BB_MORE_TERM + gotsig(0); +#endif + return 0; +} diff --git a/mount.c b/mount.c new file mode 100644 index 000000000..010757d1e --- /dev/null +++ b/mount.c @@ -0,0 +1,430 @@ +/* + 3/21/1999 Charles P. Wright + searches through fstab when -a is passed + will try mounting stuff with all fses when passed -t auto + + 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. +*/ + +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include + +const char mount_usage[] = "mount\n" +"\t\tmount [flags] special-device directory\n" +"\n" +"Flags:\n" +"\t-a:\tMount all file systems in fstab.\n" +"\t-f:\t\"Fake\" mount. Add entry to mount table but don't mount it.\n" +"\t-n:\tDon't write a mount table entry.\n" +"\t-o option:\tOne of many filesystem options, listed below.\n" +"\t-r:\tMount the filesystem read-only.\n" +"\t-t filesystem-type:\tSpecify the filesystem type.\n" +"\t-w:\tMount for reading and writing (default).\n" +"\n" +"Options for use with the \"-o\" flag:\n" +"\tasync / sync:\tWrites are asynchronous / synchronous.\n" +"\tdev / nodev:\tAllow use of special device files / disallow them.\n" +"\texec / noexec:\tAllow use of executable files / disallow them.\n" +"\tsuid / nosuid:\tAllow set-user-id-root programs / disallow them.\n" +"\tremount: Re-mount a currently-mounted filesystem, changing its flags.\n" +"\tro / rw: Mount for read-only / read-write.\n" +"\t" +"There are EVEN MORE flags that are specific to each filesystem.\n" +"You'll have to see the written documentation for those.\n"; + +struct mount_options { + const char * name; + unsigned long and; + unsigned long or; +}; + +static const struct mount_options mount_options[] = { + { "async", ~MS_SYNCHRONOUS,0 }, + { "defaults", ~0, 0 }, + { "dev", ~MS_NODEV, 0 }, + { "exec", ~MS_NOEXEC, 0 }, + { "nodev", ~0, MS_NODEV }, + { "noexec", ~0, MS_NOEXEC }, + { "nosuid", ~0, MS_NOSUID }, + { "remount", ~0, MS_REMOUNT }, + { "ro", ~0, MS_RDONLY }, + { "rw", ~MS_RDONLY, 0 }, + { "suid", ~MS_NOSUID, 0 }, + { "sync", ~0, MS_SYNCHRONOUS }, + { 0, 0, 0 } +}; + +static void +show_flags(unsigned long flags, char * buffer) +{ + const struct mount_options * f = mount_options; + while ( f->name ) { + if ( flags & f->and ) { + int length = strlen(f->name); + memcpy(buffer, f->name, length); + buffer += length; + *buffer++ = ','; + *buffer = '\0'; + } + f++; + } +} + +static void +one_option( + char * option +,unsigned long * flags +,char * data) +{ + const struct mount_options * f = mount_options; + + while ( f->name != 0 ) { + if ( strcasecmp(f->name, option) == 0 ) { + *flags &= f->and; + *flags |= f->or; + return; + } + f++; + } + if ( *data ) { + data += strlen(data); + *data++ = ','; + } + strcpy(data, option); +} + +static void +parse_mount_options( + char * options +,unsigned long * flags +,char * data) +{ + while ( *options ) { + char * comma = strchr(options, ','); + if ( comma ) + *comma = '\0'; + one_option(options, flags, data); + if ( comma ) { + *comma = ','; + options = ++comma; + } + else + break; + } +} + +int +mount_one( + char * blockDevice +,char * directory +,char * filesystemType +,unsigned long flags +,char * string_flags +,int noMtab +,int fake) +{ + int error = 0; + int status = 0; + + char buf[255]; + + if (!fake) { + if (*filesystemType == 'a') { //Will fail on real FS starting with 'a' + + FILE *f = fopen("/proc/filesystems", "r"); + + if (f == NULL) return 1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + filesystemType = buf; + if (*filesystemType == '\t') { // Not a nodev filesystem + + while (*filesystemType && *filesystemType != '\n') filesystemType++; + *filesystemType = '\0'; + + filesystemType = buf; + filesystemType++; //hop past tab + + status = mount(blockDevice, directory, filesystemType, + flags|MS_MGC_VAL ,string_flags); + error = errno; + + if (status == 0) break; + } + } + fclose(f); + } else { + + status = mount( blockDevice, directory, filesystemType, + flags|MS_MGC_VAL ,string_flags); + error = errno; + } + } + + if ( status == 0 ) { + char * s = &string_flags[strlen(string_flags)]; + FILE * mountTable; + if ( s != string_flags ) { + *s++ = ','; + show_flags(flags, s); + } + if ( !noMtab && (mountTable = setmntent("/etc/mtab", "a+")) ) { + int length = strlen(directory); + struct mntent m; + + if ( length > 1 && directory[length - 1] == '/' ) + directory[length - 1] = '\0'; + + if ( filesystemType == 0 ) { + struct mntent * p + = findMountPoint(blockDevice, "/proc/mounts"); + + if ( p && p->mnt_type ) + filesystemType = p->mnt_type; + } + m.mnt_fsname = blockDevice; + m.mnt_dir = directory; + m.mnt_type = filesystemType ? filesystemType : "default"; + + if (*string_flags) { + m.mnt_opts = string_flags; + } else { + if ( (flags | MS_RDONLY) == flags ) + m.mnt_opts = "ro"; + else + m.mnt_opts = "rw"; + } + + m.mnt_freq = 0; + m.mnt_passno = 0; + addmntent(mountTable, &m); + endmntent(mountTable); + } + return 0; + } else { + fprintf(stderr, "Mount %s", blockDevice); + if ( filesystemType && *filesystemType ) + fprintf(stderr, " (type %s)", filesystemType); + + fprintf( + stderr + ," on %s: " + ,directory); + + switch ( error ) { + case EPERM: + if (geteuid() == 0) + fprintf( + stderr + ,"mount point %s is not a directory" + ,blockDevice); + else + fprintf( + stderr + ,"must be superuser to use mount"); + break; + case EBUSY: + fprintf( + stderr + ,"%s already mounted or %s busy" + ,blockDevice + ,directory); + break; + case ENOENT: + { + struct stat statbuf; + if ( stat(directory, &statbuf) != 0 ) + fprintf( + stderr + ,"directory %s does not exist" + ,directory); + else if ( stat(blockDevice, &statbuf) != 0 ) + fprintf( + stderr + ,"block device %s does not exist" + ,blockDevice); + else + fprintf( + stderr + ,"%s is not mounted on %s, but the mount table says it is." + ,blockDevice + ,directory); + break; + } + case ENOTDIR: + fprintf( + stderr + ,"%s is not a directory" + ,directory); + break; + case EINVAL: + fprintf( + stderr + ,"wrong filesystem type, or bad superblock on %s" + ,blockDevice); + break; + case EMFILE: + fprintf(stderr, "mount table full"); + break; + case EIO: + fprintf( + stderr + ,"I/O error reading %s" + ,blockDevice); + break; + case ENODEV: + { + FILE * f = fopen("/proc/filesystems", "r"); + + fprintf( + stderr + ,"filesystem type %s not in kernel.\n" + ,filesystemType); + fprintf(stderr, "Do you need to load a module?\n"); + if ( f ) { + char buf[100]; + + fprintf( + stderr + ,"Here are the filesystem types the kernel" + " can mount:\n"); + while ( fgets(buf, sizeof(buf), f) != 0 ) + fprintf(stderr, "\t%s", buf); + fclose(f); + } + break; + } + case ENOTBLK: + fprintf( + stderr + ,"%s is not a block device" + ,blockDevice); + break; + case ENXIO: + fprintf( + stderr + ,"%s is not a valid block device" + ,blockDevice); + break; + default: + fputs(strerror(errno), stderr); + } + putc('\n', stderr); + return -1; + } +} + +extern int +mount_main(struct FileInfo * i, int argc, char * * argv) +{ + char string_flags[1024]; + unsigned long flags = 0; + char * filesystemType = "auto"; + int fake = 0; + int noMtab = 0; + int all = 0; + + *string_flags = '\0'; + + if ( argc == 1 ) { + FILE * mountTable; + if ( (mountTable = setmntent("/etc/mtab", "r")) ) { + struct mntent * m; + while ( (m = getmntent(mountTable)) != 0 ) { + printf( + "%s on %s type %s (%s)\n" + ,m->mnt_fsname + ,m->mnt_dir + ,m->mnt_type + ,m->mnt_opts); + } + endmntent(mountTable); + } + return 0; + } + + while ( argc >= 2 && argv[1][0] == '-' ) { + switch ( argv[1][1] ) { + case 'f': + fake = 1; + break; + case 'n': + noMtab = 1; + break; + case 'o': + if ( argc < 3 ) { + usage(mount_usage); + return 1; + } + parse_mount_options(argv[2], &flags, string_flags); + argc--; + argv++; + break; + case 'r': + flags |= MS_RDONLY; + break; + case 't': + if ( argc < 3 ) { + usage(mount_usage); + return 1; + } + filesystemType = argv[2]; + argc--; + argv++; + break; + case 'v': + break; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'a': + all = 1; + break; + default: + usage(mount_usage); + return 1; + } + argc--; + argv++; + } + + if (all == 1) { + struct mntent *m; + FILE *f = setmntent("/etc/fstab", "r"); + + if (f == NULL) { + return 1; + } + + // FIXME: Combine read routine (make new function) with unmount_all to save space. + + while ((m = getmntent(f)) != NULL) { + // If the file system isn't noauto, and isn't mounted on /, mount it + if ((!strstr(m->mnt_opts, "noauto")) && (m->mnt_dir[1] != '\0') + && !((m->mnt_type[0] == 's') && (m->mnt_type[1] == 'w')) + && !((m->mnt_type[0] == 'n') && (m->mnt_type[1] == 'f'))) { + mount_one(m->mnt_fsname, m->mnt_dir, m->mnt_type, flags, m->mnt_opts, noMtab, fake); + } + } + + endmntent(f); + } else { + if ( argc >= 3 ) { + if ( mount_one( argv[1], argv[2], filesystemType, flags, string_flags, noMtab, fake) == 0 ) + return 0; + else + return 1; + } else { + usage(mount_usage); + return 1; + } + } + return 0; +} diff --git a/mt.c b/mt.c new file mode 100644 index 000000000..7d75fbd3d --- /dev/null +++ b/mt.c @@ -0,0 +1,98 @@ +#include "internal.h" +#include +#include +#include + +const char mt_usage[] = "mt [-f device] opcode value\n"; + +struct mt_opcodes { + char * name; + short value; +}; + +/* missing: eod/seod, stoptions, stwrthreshold, densities */ +static const struct mt_opcodes opcodes[] = { + { "bsf", MTBSF }, + { "bsfm", MTBSFM }, + { "bsr", MTBSR }, + { "bss", MTBSS }, + { "datacompression", MTCOMPRESSION }, + { "eom", MTEOM }, + { "erase", MTERASE }, + { "fsf", MTFSF }, + { "fsfm", MTFSFM }, + { "fsr", MTFSR }, + { "fss", MTFSS }, + { "load", MTLOAD }, + { "lock", MTLOCK }, + { "mkpart", MTMKPART }, + { "nop", MTNOP }, + { "offline",MTOFFL }, + { "rewoffline",MTOFFL }, + { "ras1", MTRAS1 }, + { "ras2", MTRAS2 }, + { "ras3", MTRAS3 }, + { "reset", MTRESET }, + { "retension", MTRETEN }, + { "rew", MTREW }, + { "seek", MTSEEK }, + { "setblk", MTSETBLK }, + { "setdensity", MTSETDENSITY }, + { "drvbuffer", MTSETDRVBUFFER }, + { "setpart", MTSETPART }, + { "tell", MTTELL }, + { "wset", MTWSM }, + { "unload", MTUNLOAD }, + { "unlock", MTUNLOCK }, + { "eof", MTWEOF }, + { "weof", MTWEOF }, + { 0, 0 } +}; + +extern int +mt_main(struct FileInfo * i, int argc, char * * argv) +{ + const char * file = "/dev/tape"; + const struct mt_opcodes * code = opcodes; + struct mtop op; + int fd; + + if ( strcmp(argv[1], "-f") == 0 ) { + if ( argc < 4 ) { + usage(mt_usage); + return 1; + } + file = argv[2]; + argv += 2; + argc -= 2; + } + + while ( code->name != 0 ) { + if ( strcmp(code->name, argv[1]) == 0 ) + break; + code++; + } + + if ( code->name == 0 ) { + fprintf(stderr, "mt: unrecognized opcode %s.\n", argv[1]); + return 1; + } + + op.mt_op = code->value; + if ( argc >= 3 ) + op.mt_count = atoi(argv[2]); + else + op.mt_count = 1; /* One, not zero, right? */ + + if ( (fd = open(file, O_RDONLY, 0)) < 0 ) { + name_and_error(file); + return 1; + } + + if ( ioctl(fd, MTIOCTOP, &op) != 0 ) { + name_and_error(file); + return 1; + } + + return 0; +} diff --git a/mv.c b/mv.c new file mode 100644 index 000000000..22c4a1207 --- /dev/null +++ b/mv.c @@ -0,0 +1,38 @@ +#include "internal.h" +#include +#include + +const char mv_usage[] = "mv source-file destination-file\n" +"\t\tmv source-file [source-file ...] destination-directory\n" +"\n" +"\tMove the source files to the destination.\n" +"\n"; + +extern int +mv_fn(const struct FileInfo * i) +{ + struct stat destination_stat; + char d[1024]; + struct FileInfo n; + + if ( stat(i->destination, &destination_stat) == 0 ) { + if ( i->stat.st_ino == destination_stat.st_ino + && i->stat.st_dev == destination_stat.st_dev ) + return 0; /* Move file to itself. */ + } + if ( (destination_stat.st_mode & S_IFMT) == S_IFDIR ) { + n = *i; + n.destination = join_paths(d, i->destination, basename(i->source)); + i = &n; + } + if ( rename(i->source, i->destination) == 0 ) + return 0; + else if ( errno == EXDEV && is_a_directory(i->source) ) { + fprintf(stderr + ,"%s: Can't move directory across filesystems.\n" + ,i->source); + return 1; + } + else + return cp_fn(i); +} diff --git a/postprocess.c b/postprocess.c new file mode 100644 index 000000000..bbc87e689 --- /dev/null +++ b/postprocess.c @@ -0,0 +1,41 @@ +#include "internal.h" + +extern int +post_process(const struct FileInfo * i) +{ + int status = 0; + + if ( i->destination == 0 || *i->destination == 0 ) + return 0; + + if ( status == 0 && i->changeMode ) { + mode_t mode = i->stat.st_mode & 07777; + mode &= i->andWithMode; + mode |= i->orWithMode; + status = chmod(i->destination, mode); + + if ( status != 0 && i->complainInPostProcess && !i->force ) { + name_and_error(i->destination); + return 1; + } + } + + if ( i->changeUserID || i->changeGroupID ) { + uid_t uid = i->stat.st_uid; + gid_t gid = i->stat.st_gid; + + if ( i->changeUserID ) + uid = i->userID; + if ( i->changeGroupID ) + gid = i->groupID; + + status = chown(i->destination, uid, gid); + + if ( status != 0 && i->complainInPostProcess && !i->force ) { + name_and_error(i->destination); + return 1; + } + } + + return status; +} diff --git a/printf.c b/printf.c new file mode 100644 index 000000000..e79843c80 --- /dev/null +++ b/printf.c @@ -0,0 +1,531 @@ +// I may still need some more cleaning...fix my error checking + +#include "internal.h" +#ifdef BB_PRINTF + +/* printf - format and print data + Copyright (C) 90, 91, 92, 93, 94, 95, 1996 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Usage: printf format [argument...] + + A front end to the printf function that lets it be used from the shell. + + Backslash escapes: + + \" = double quote + \\ = backslash + \a = alert (bell) + \b = backspace + \c = produce no further output + \f = form feed + \n = new line + \r = carriage return + \t = horizontal tab + \v = vertical tab + \0ooo = octal number (ooo is 0 to 3 digits) + \xhhh = hexadecimal number (hhh is 1 to 3 digits) + + Additional directive: + + %b = print an argument string, interpreting backslash escapes + + The `format' argument is re-used as many times as necessary + to convert all of the given arguments. + + David MacKenzie */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef S_IFMT +# define S_IFMT 0170000 +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +#define IN_CTYPE_DOMAIN(c) 1 + +#ifdef isblank +# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c)) +#else +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) +#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c)) +#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) +#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c)) +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0') +#define octtobin(c) ((c) - '0') + +char *xmalloc (); + +static double xstrtod __P ((char *s)); +static int print_esc __P ((char *escstart)); +static int print_formatted __P ((char *format, int argc, char **argv)); +static long xstrtol __P ((char *s)); +static unsigned long xstrtoul __P ((char *s)); +static void print_direc __P ((char *start, size_t length, int field_width, int precision, char *argument)); +static void print_esc_char __P ((int c)); +static void print_esc_string __P ((char *str)); +static void verify __P ((char *s, char *end)); + +/* The value to return to the calling program. */ +static int exit_status; + +const char printf_usage[] = "Usage: printf format [argument...]\n"; + +int +printf_main(struct FileInfo * i, int argc, char * * argv) +{ + char *format; + int args_used; + + exit_status = 0; + + format = argv[1]; + argc -= 2; + argv += 2; + + do + { + args_used = print_formatted (format, argc, argv); + argc -= args_used; + argv += args_used; + } + while (args_used > 0 && argc > 0); + +/* + if (argc > 0) + fprintf(stderr, "excess args ignored"); +*/ + + exit (exit_status); +} + +/* Print the text in FORMAT, using ARGV (with ARGC elements) for + arguments to any `%' directives. + Return the number of elements of ARGV used. */ + +static int +print_formatted (char *format, int argc, char **argv) +{ + int save_argc = argc; /* Preserve original value. */ + char *f; /* Pointer into `format'. */ + char *direc_start; /* Start of % directive. */ + size_t direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*', or -1 if none. */ + int precision; /* Arg to second '*', or -1 if none. */ + + for (f = format; *f; ++f) + { + switch (*f) + { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = -1; + if (*f == '%') + { + putchar ('%'); + break; + } + if (*f == 'b') + { + if (argc > 0) + { + print_esc_string (*argv); + ++argv; + --argc; + } + break; + } + if (strchr ("-+ #", *f)) + { + ++f; + ++direc_length; + } + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + field_width = xstrtoul (*argv); + ++argv; + --argc; + } + else + field_width = 0; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + if (*f == '.') + { + ++f; + ++direc_length; + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + precision = xstrtoul (*argv); + ++argv; + --argc; + } + else + precision = 0; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + } + if (*f == 'l' || *f == 'L' || *f == 'h') + { + ++f; + ++direc_length; + } + /* + if (!strchr ("diouxXfeEgGcs", *f)) + fprintf(stderr, "%%%c: invalid directive", *f); + */ + ++direc_length; + if (argc > 0) + { + print_direc (direc_start, direc_length, field_width, + precision, *argv); + ++argv; + --argc; + } + else + print_direc (direc_start, direc_length, field_width, + precision, ""); + break; + + case '\\': + f += print_esc (f); + break; + + default: + putchar (*f); + } + } + + return save_argc - argc; +} + +/* Print a \ escape sequence starting at ESCSTART. + Return the number of characters in the escape sequence + besides the backslash. */ + +static int +print_esc (char *escstart) +{ + register char *p = escstart + 1; + int esc_value = 0; /* Value of \nnn escape. */ + int esc_length; /* Length of \nnn escape. */ + + /* \0ooo and \xhhh escapes have maximum length of 3 chars. */ + if (*p == 'x') + { + for (esc_length = 0, ++p; + esc_length < 3 && ISXDIGIT (*p); + ++esc_length, ++p) + esc_value = esc_value * 16 + hextobin (*p); +/* if (esc_length == 0) + fprintf(stderr, "missing hex in esc"); +*/ + putchar (esc_value); + } + else if (*p == '0') + { + for (esc_length = 0, ++p; + esc_length < 3 && isodigit (*p); + ++esc_length, ++p) + esc_value = esc_value * 8 + octtobin (*p); + putchar (esc_value); + } + else if (strchr ("\"\\abcfnrtv", *p)) + print_esc_char (*p++); +/* else + fprintf(stderr, "\\%c: invalid esc", *p); +*/ + return p - escstart - 1; +} + +/* Output a single-character \ escape. */ + +static void +print_esc_char (int c) +{ + switch (c) + { + case 'a': /* Alert. */ + putchar (7); + break; + case 'b': /* Backspace. */ + putchar (8); + break; + case 'c': /* Cancel the rest of the output. */ + exit (0); + break; + case 'f': /* Form feed. */ + putchar (12); + break; + case 'n': /* New line. */ + putchar (10); + break; + case 'r': /* Carriage return. */ + putchar (13); + break; + case 't': /* Horizontal tab. */ + putchar (9); + break; + case 'v': /* Vertical tab. */ + putchar (11); + break; + default: + putchar (c); + break; + } +} + +/* Print string STR, evaluating \ escapes. */ + +static void +print_esc_string (char *str) +{ + for (; *str; str++) + if (*str == '\\') + str += print_esc (str); + else + putchar (*str); +} + +static void +print_direc (char *start, size_t length, int field_width, int precision, char *argument) +{ + char *p; /* Null-terminated copy of % directive. */ + + p = xmalloc ((unsigned) (length + 1)); + strncpy (p, start, length); + p[length] = 0; + + switch (p[length - 1]) + { + case 'd': + case 'i': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtol (argument)); + else + printf (p, precision, xstrtol (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtol (argument)); + else + printf (p, field_width, precision, xstrtol (argument)); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtoul (argument)); + else + printf (p, precision, xstrtoul (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtoul (argument)); + else + printf (p, field_width, precision, xstrtoul (argument)); + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + if (field_width < 0) + { + if (precision < 0) + printf (p, xstrtod (argument)); + else + printf (p, precision, xstrtod (argument)); + } + else + { + if (precision < 0) + printf (p, field_width, xstrtod (argument)); + else + printf (p, field_width, precision, xstrtod (argument)); + } + break; + + case 'c': + printf (p, *argument); + break; + + case 's': + if (field_width < 0) + { + if (precision < 0) + printf (p, argument); + else + printf (p, precision, argument); + } + else + { + if (precision < 0) + printf (p, field_width, argument); + else + printf (p, field_width, precision, argument); + } + break; + } + + free (p); +} + +static unsigned long +xstrtoul (char *s) +{ + char *end; + unsigned long val; + + errno = 0; + val = strtoul (s, &end, 0); + verify (s, end); + return val; +} + +static long +xstrtol (char *s) +{ + char *end; + long val; + + errno = 0; + val = strtol (s, &end, 0); + verify (s, end); + return val; +} + +static double +xstrtod (char *s) +{ + char *end; + double val; + + errno = 0; + val = strtod (s, &end); + verify (s, end); + return val; +} + +static void +verify (char *s, char *end) +{ + if (errno) + { + fprintf(stderr, "%s", s); + exit_status = 1; + } + else if (*end) + { + /* + if (s == end) + fprintf(stderr, "%s: expected numeric", s); + else + fprintf(stderr, "%s: not completely converted", s); + */ + exit_status = 1; + } +} + +#endif diff --git a/procps/kill.c b/procps/kill.c new file mode 100644 index 000000000..da025fafc --- /dev/null +++ b/procps/kill.c @@ -0,0 +1,140 @@ +#include "internal.h" +#include +#include +#include +#include + +const char kill_usage[] = "kill [-signal] process-id [process-id ...]\n"; + +struct signal_name { + const char * name; + int number; +}; + +const struct signal_name signames[] = { + { "HUP", SIGHUP }, + { "INT", SIGINT }, + { "QUIT", SIGQUIT }, + { "ILL", SIGILL }, + { "TRAP", SIGTRAP }, + { "ABRT", SIGABRT }, +#ifndef __alpha__ + { "IOT", SIGIOT }, +#endif +#if defined(sparc) || defined(__alpha__) + { "EMT", SIGEMT }, +#else + { "BUS", SIGBUS }, +#endif + { "FPE", SIGFPE }, + { "KILL", SIGKILL }, +#if defined(sparc) || defined(__alpha__) + { "BUS", SIGBUS }, +#else + { "USR1", SIGUSR1 }, +#endif + { "SEGV", SIGSEGV }, +#if defined(sparc) || defined(__alpha__) + { "SYS", SIGSYS }, +#else + { "USR2", SIGUSR2 }, +#endif + { "PIPE", SIGPIPE }, + { "ALRM", SIGALRM }, + { "TERM", SIGTERM }, +#if defined(sparc) || defined(__alpha__) + { "URG", SIGURG }, + { "STOP", SIGSTOP }, + { "TSTP", SIGTSTP }, + { "CONT", SIGCONT }, + { "CHLD", SIGCHLD }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, + { "IO", SIGIO }, +# ifndef __alpha__ + { "POLL", SIGIO }, +# endif + { "XCPU", SIGXCPU }, + { "XFSZ", SIGXFSZ }, + { "VTALRM", SIGVTALRM }, + { "PROF", SIGPROF }, + { "WINCH", SIGWINCH }, +# ifdef __alpha__ + { "INFO", SIGINFO }, +# else + { "LOST", SIGLOST }, +# endif + { "USR1", SIGUSR1 }, + { "USR2", SIGUSR2 }, +#else + { "STKFLT", SIGSTKFLT }, + { "CHLD", SIGCHLD }, + { "CONT", SIGCONT }, + { "STOP", SIGSTOP }, + { "TSTP", SIGTSTP }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, + { "URG", SIGURG }, + { "XCPU", SIGXCPU }, + { "XFSZ", SIGXFSZ }, + { "VTALRM", SIGVTALRM }, + { "PROF", SIGPROF }, + { "WINCH", SIGWINCH }, + { "IO", SIGIO }, + { "POLL", SIGPOLL }, + { "PWR", SIGPWR }, + { "UNUSED", SIGUNUSED }, +#endif + { 0, 0 } +}; + +extern int +kill_main(struct FileInfo * i, int argc, char * * argv) +{ + int had_error = 0; + int sig = SIGTERM; + if ( argv[1][0] == '-' ) { + if ( argv[1][1] >= '0' && argv[1][1] <= '9' ) { + sig = atoi(&argv[1][1]); + if ( sig < 0 || sig >= NSIG ) { + usage(kill_usage); + exit(-1); + } + } + else { + const struct signal_name * s = signames; + for ( ; ; ) { + if ( strcmp(s->name, &argv[1][1]) == 0 ) { + sig = s->number; + break; + } + s++; + if ( s->name == 0 ) { + usage(kill_usage); + exit(-1); + } + } + } + argv++; + argc--; + + } + while ( argc > 1 ) { + int pid; + if ( argv[1][0] < '0' || argv[1][0] > '9' ) { + usage(kill_usage); + exit(-1); + } + pid = atoi(argv[1]); + if ( kill(pid, sig) != 0 ) { + had_error = 1; + perror(argv[1]); + } + argv++; + argc--; + } + if ( had_error ) + return -1; + else + return 0; +} diff --git a/pwd.c b/pwd.c new file mode 100644 index 000000000..d9ab54e48 --- /dev/null +++ b/pwd.c @@ -0,0 +1,18 @@ +#include "internal.h" +#include + +const char pwd_usage[] = "Print the current directory.\n"; + +extern int +pwd_main(struct FileInfo * i, int argc, char * * argv) +{ + char buf[1024]; + + if ( getcwd(buf, sizeof(buf)) == NULL ) { + name_and_error("get working directory"); + return 1; + } + + printf("%s\n", buf); + return 0; +} diff --git a/reboot.c b/reboot.c new file mode 100644 index 000000000..0388fbce7 --- /dev/null +++ b/reboot.c @@ -0,0 +1,12 @@ +#include "internal.h" +#include + +const char reboot_usage[] = "reboot\n" +"\n\t" +"\treboot the system.\n"; + +extern int +reboot_main(struct FileInfo * i, int argc, char * * argv) +{ + return kill(1, SIGUSR2); +} diff --git a/rm.c b/rm.c new file mode 100644 index 000000000..dc35b0297 --- /dev/null +++ b/rm.c @@ -0,0 +1,30 @@ +#include "internal.h" +#include + +const char rm_usage[] = "rm [-r] file [file ...]\n" +"\n" +"\tDelete files.\n" +"\n" +"\t-r:\tRecursively remove files and directories.\n"; + +extern int +rm_main(struct FileInfo * i, int argc, char * * argv) +{ + i->processDirectoriesAfterTheirContents = 1; + return monadic_main(i, argc, argv); +} + +extern int +rm_fn(const struct FileInfo * i) +{ + if ( i->recursive + && !i->isSymbolicLink + && (i->stat.st_mode & S_IFMT) == S_IFDIR ) + return rmdir_fn(i); + else if ( unlink(i->source) != 0 && errno != ENOENT && !i->force ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} diff --git a/rmdir.c b/rmdir.c new file mode 100644 index 000000000..069e68546 --- /dev/null +++ b/rmdir.c @@ -0,0 +1,17 @@ +#include "internal.h" +#include + +const char rmdir_usage[] = "rmdir directory [directory ...]\n" +"\n" +"\tDelete directories.\n"; + +extern int +rmdir_fn(const struct FileInfo * i) +{ + if ( rmdir(i->source) != 0 && errno != ENOENT && !i->force ) { + name_and_error(i->source); + return 1; + } + else + return 0; +} diff --git a/sleep.c b/sleep.c new file mode 100644 index 000000000..e48e14b2f --- /dev/null +++ b/sleep.c @@ -0,0 +1,15 @@ +#include "internal.h" +#include + +const char sleep_usage[] = "sleep seconds\n" +"\n" +"\tPause program execution for the given number of seconds.\n"; + +extern int +sleep_main(struct FileInfo * i, int argc, char * * argv) +{ + if ( sleep(atoi(argv[1])) != 0 ) + return -1; + else + return 0; +} diff --git a/smtpout b/smtpout new file mode 100644 index 000000000..635a83c6f --- /dev/null +++ b/smtpout @@ -0,0 +1,2 @@ +echo '.' >smtpout echo 'QUIT' >smtpout +kjfjkjd diff --git a/swapoff.c b/swapoff.c new file mode 100644 index 000000000..55124d0b8 --- /dev/null +++ b/swapoff.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include "internal.h" + +const char swapoff_usage[] = "swapoff block-device\n" +"\n" +"\tStop swapping virtual memory pages on the given device.\n"; + +extern int +swapoff_fn(const struct FileInfo * i) +{ + struct mntent entries[100]; + int count = 0; + FILE * swapsTable = setmntent("/etc/swaps", "r"); + struct mntent * m; + + if (!(swapoff(i->source))) { + if ( swapsTable == 0 ) { + fprintf(stderr, "/etc/swaps: %s\n", strerror(errno)); + return 1; + } + while ( (m = getmntent(swapsTable)) != 0 ) { + entries[count].mnt_fsname = strdup(m->mnt_fsname); + entries[count].mnt_dir = strdup(m->mnt_dir); + entries[count].mnt_type = strdup(m->mnt_type); + entries[count].mnt_opts = strdup(m->mnt_opts); + entries[count].mnt_freq = m->mnt_freq; + entries[count].mnt_passno = m->mnt_passno; + count++; + } + endmntent(swapsTable); + if ( (swapsTable = setmntent("/etc/swaps", "w")) ) { + int id; + for ( id = 0; id < count; id++ ) { + int result = + (strcmp(entries[id].mnt_fsname, i->source)==0 + ||strcmp(entries[id].mnt_dir, i->source)==0); + if ( result ) + continue; + else + addmntent(swapsTable, &entries[id]); + } + endmntent(swapsTable); + } + else if ( errno != EROFS ) + fprintf(stderr, "/etc/swaps: %s\n", strerror(errno)); + return (0); + } + return (-1); +} diff --git a/swapon.c b/swapon.c new file mode 100644 index 000000000..78360a55f --- /dev/null +++ b/swapon.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "internal.h" + +const char swapon_usage[] = "swapon block-device\n" +"\n" +"\tSwap virtual memory pages on the given device.\n"; + +extern int +swapon_fn(const struct FileInfo * i) +{ + FILE *swapsTable; + struct mntent m; + + if (!(swapon(i->source, 0))) { + if ((swapsTable = setmntent("/etc/swaps", "a+"))) { + /* Needs the cast to avoid warning about conversion from + * const char* to just char* + */ + m.mnt_fsname = (char*)i->source; + m.mnt_dir = "none"; + m.mnt_type = "swap"; + m.mnt_opts = "sw"; + m.mnt_freq = 0; + m.mnt_passno = 0; + addmntent(swapsTable, &m); + endmntent(swapsTable); + } + return (0); + } + return (-1); +} + diff --git a/sync.c b/sync.c new file mode 100644 index 000000000..6fa5b380b --- /dev/null +++ b/sync.c @@ -0,0 +1,11 @@ +#include "internal.h" + +const char sync_usage[] = "sync\n" +"\n" +"\tWrite all buffered filesystem blocks to disk.\n"; + +extern int +sync_main(struct FileInfo * i, int argc, char * * argv) +{ + return sync(); +} diff --git a/tar.c b/tar.c new file mode 100644 index 000000000..03da96735 --- /dev/null +++ b/tar.c @@ -0,0 +1,1425 @@ +/* + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "tar" command, taken from sash. + * This allows creation, extraction, and listing of tar files. + * + * Permission to distribute this code under the GPL has been granted. + * Modified for busybox by Erik Andersen + */ + + +#include "internal.h" + +#ifdef BB_TAR + +const char tar_usage[] = +"Create, extract, or list files from a TAR file\n\n" +"usage: tar -[cxtvOf] [tarFileName] [FILE] ...\n" +"\tc=create, x=extract, t=list contents, v=verbose,\n" +"\tO=extract to stdout, f=tarfile or \"-\" for stdin\n"; + + + +#include +#include +#include +#include +#include +#include + +/* + * Tar file constants. + */ +#define TAR_BLOCK_SIZE 512 +#define TAR_NAME_SIZE 100 + + +/* + * The POSIX (and basic GNU) tar header format. + * This structure is always embedded in a TAR_BLOCK_SIZE sized block + * with zero padding. We only process this information minimally. + */ +typedef struct +{ + char name[TAR_NAME_SIZE]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checkSum[8]; + char typeFlag; + char linkName[TAR_NAME_SIZE]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devMajor[8]; + char devMinor[8]; + char prefix[155]; +} TarHeader; + +#define TAR_MAGIC "ustar" +#define TAR_VERSION "00" + +#define TAR_TYPE_REGULAR '0' +#define TAR_TYPE_HARD_LINK '1' +#define TAR_TYPE_SOFT_LINK '2' + + +/* + * Static data. + */ +static BOOL listFlag; +static BOOL extractFlag; +static BOOL createFlag; +static BOOL verboseFlag; +static BOOL tostdoutFlag; + +static BOOL inHeader; +static BOOL badHeader; +static BOOL errorFlag; +static BOOL skipFileFlag; +static BOOL warnedRoot; +static BOOL eofFlag; +static long dataCc; +static int outFd; +static char outName[TAR_NAME_SIZE]; + + +/* + * Static data associated with the tar file. + */ +static const char * tarName; +static int tarFd; +static dev_t tarDev; +static ino_t tarInode; + + +/* + * Local procedures to restore files from a tar file. + */ +static void readTarFile(int fileCount, char ** fileTable); +static void readData(const char * cp, int count); +static void createPath(const char * name, int mode); +static long getOctal(const char * cp, int len); + +static void readHeader(const TarHeader * hp, + int fileCount, char ** fileTable); + + +/* + * Local procedures to save files into a tar file. + */ +static void saveFile(const char * fileName, BOOL seeLinks); + +static void saveRegularFile(const char * fileName, + const struct stat * statbuf); + +static void saveDirectory(const char * fileName, + const struct stat * statbuf); + +static BOOL wantFileName(const char * fileName, + int fileCount, char ** fileTable); + +static void writeHeader(const char * fileName, + const struct stat * statbuf); + +static void writeTarFile(int fileCount, char ** fileTable); +static void writeTarBlock(const char * buf, int len); +static BOOL putOctal(char * cp, int len, long value); +extern const char * modeString(int mode); +extern const char * timeString(time_t timeVal); +extern int fullWrite(int fd, const char * buf, int len); +extern int fullRead(int fd, char * buf, int len); + + +extern int +tar_main(struct FileInfo *unused, int argc, char ** argv) +{ + const char * options; + + argc--; + argv++; + + if (argc < 1) + { + fprintf(stderr, "%s", tar_usage); + return 1; + } + + + errorFlag = FALSE; + extractFlag = FALSE; + createFlag = FALSE; + listFlag = FALSE; + verboseFlag = FALSE; + tostdoutFlag = FALSE; + tarName = NULL; + tarDev = 0; + tarInode = 0; + tarFd = -1; + + /* + * Parse the options. + */ + options = *argv++; + argc--; + + if (**argv == '-') { + for (; *options; options++) + { + switch (*options) + { + case 'f': + if (tarName != NULL) + { + fprintf(stderr, "Only one 'f' option allowed\n"); + + return 1; + } + + tarName = *argv++; + argc--; + + break; + + case 't': + listFlag = TRUE; + break; + + case 'x': + extractFlag = TRUE; + break; + + case 'c': + createFlag = TRUE; + break; + + case 'v': + verboseFlag = TRUE; + break; + + case 'O': + tostdoutFlag = TRUE; + break; + + case '-': + break; + + default: + fprintf(stderr, "Unknown tar flag '%c'\n", *options); + + return 1; + } + } + } + + /* + * Validate the options. + */ + if (extractFlag + listFlag + createFlag != 1) + { + fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n"); + + return 1; + } + + /* + * Do the correct type of action supplying the rest of the + * command line arguments as the list of files to process. + */ + if (createFlag) + writeTarFile(argc, argv); + else + readTarFile(argc, argv); + if (errorFlag) + fprintf(stderr, "\n"); + return( errorFlag); +} + + +/* + * Read a tar file and extract or list the specified files within it. + * If the list is empty than all files are extracted or listed. + */ +static void +readTarFile(int fileCount, char ** fileTable) +{ + const char * cp; + int cc; + int inCc; + int blockSize; + char buf[BUF_SIZE]; + + skipFileFlag = FALSE; + badHeader = FALSE; + warnedRoot = FALSE; + eofFlag = FALSE; + inHeader = TRUE; + inCc = 0; + dataCc = 0; + outFd = -1; + blockSize = sizeof(buf); + cp = buf; + + /* + * Open the tar file for reading. + */ + if ( (tarName==NULL) || !strcmp( tarName, "-") ) { + tarFd = STDIN; + } + else + tarFd = open(tarName, O_RDONLY); + + if (tarFd < 0) + { + perror(tarName); + errorFlag = TRUE; + return; + } + + /* + * Read blocks from the file until an end of file header block + * has been seen. (A real end of file from a read is an error.) + */ + while (!eofFlag) + { + /* + * Read the next block of data if necessary. + * This will be a large block if possible, which we will + * then process in the small tar blocks. + */ + if (inCc <= 0) + { + cp = buf; + inCc = fullRead(tarFd, buf, blockSize); + + if (inCc < 0) + { + perror(tarName); + errorFlag=TRUE; + goto done; + } + + if (inCc == 0) + { + fprintf(stderr, + "Unexpected end of file from \"%s\"", + tarName); + errorFlag=TRUE; + goto done; + } + } + + /* + * If we are expecting a header block then examine it. + */ + if (inHeader) + { + readHeader((const TarHeader *) cp, fileCount, fileTable); + + cp += TAR_BLOCK_SIZE; + inCc -= TAR_BLOCK_SIZE; + + continue; + } + + /* + * We are currently handling the data for a file. + * Process the minimum of the amount of data we have available + * and the amount left to be processed for the file. + */ + cc = inCc; + + if (cc > dataCc) + cc = dataCc; + + readData(cp, cc); + + /* + * If the amount left isn't an exact multiple of the tar block + * size then round it up to the next block boundary since there + * is padding at the end of the file. + */ + if (cc % TAR_BLOCK_SIZE) + cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE); + + cp += cc; + inCc -= cc; + } + +done: + /* + * Close the tar file if needed. + */ + if ((tarFd >= 0) && (close(tarFd) < 0)) + perror(tarName); + + /* + * Close the output file if needed. + * This is only done here on a previous error and so no + * message is required on errors. + */ + if (tostdoutFlag==FALSE) { + if (outFd >= 0) + (void) close(outFd); + } +} + + +/* + * Examine the header block that was just read. + * This can specify the information for another file, or it can mark + * the end of the tar file. + */ +static void +readHeader(const TarHeader * hp, int fileCount, char ** fileTable) +{ + int mode; + int uid; + int gid; + int checkSum; + long size; + time_t mtime; + const char * name; + int cc; + BOOL hardLink; + BOOL softLink; + + /* + * If the block is completely empty, then this is the end of the + * archive file. If the name is null, then just skip this header. + */ + name = hp->name; + + if (*name == '\0') + { + for (cc = TAR_BLOCK_SIZE; cc > 0; cc--) + { + if (*name++) + return; + } + + eofFlag = TRUE; + + return; + } + + /* + * There is another file in the archive to examine. + * Extract the encoded information and check it. + */ + mode = getOctal(hp->mode, sizeof(hp->mode)); + uid = getOctal(hp->uid, sizeof(hp->uid)); + gid = getOctal(hp->gid, sizeof(hp->gid)); + size = getOctal(hp->size, sizeof(hp->size)); + mtime = getOctal(hp->mtime, sizeof(hp->mtime)); + checkSum = getOctal(hp->checkSum, sizeof(hp->checkSum)); + + if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) + { + if (!badHeader) + fprintf(stderr, "Bad tar header, skipping\n"); + + badHeader = TRUE; + + return; + } + + badHeader = FALSE; + skipFileFlag = FALSE; + + /* + * Check for the file modes. + */ + hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) || + (hp->typeFlag == TAR_TYPE_HARD_LINK - '0')); + + softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) || + (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0')); + + /* + * Check for a directory or a regular file. + */ + if (name[strlen(name) - 1] == '/') + mode |= S_IFDIR; + else if ((mode & S_IFMT) == 0) + mode |= S_IFREG; + + /* + * Check for absolute paths in the file. + * If we find any, then warn the user and make them relative. + */ + if (*name == '/') + { + while (*name == '/') + name++; + + if (!warnedRoot) + { + fprintf(stderr, + "Absolute path detected, removing leading slashes\n"); + } + + warnedRoot = TRUE; + } + + /* + * See if we want this file to be restored. + * If not, then set up to skip it. + */ + if (!wantFileName(name, fileCount, fileTable)) + { + if (!hardLink && !softLink && S_ISREG(mode)) + { + inHeader = (size == 0); + dataCc = size; + } + + skipFileFlag = TRUE; + + return; + } + + /* + * This file is to be handled. + * If we aren't extracting then just list information about the file. + */ + if (!extractFlag) + { + if (verboseFlag) + { + printf("%s %3d/%-d %9ld %s %s", modeString(mode), + uid, gid, size, timeString(mtime), name); + } + else + printf("%s", name); + + if (hardLink) + printf(" (link to \"%s\")", hp->linkName); + else if (softLink) + printf(" (symlink to \"%s\")", hp->linkName); + else if (S_ISREG(mode)) + { + inHeader = (size == 0); + dataCc = size; + } + + printf("\n"); + + return; + } + + /* + * We really want to extract the file. + */ + if (verboseFlag) + printf("x %s\n", name); + + if (hardLink) + { + if (link(hp->linkName, name) < 0) + perror(name); + + return; + } + + if (softLink) + { +#ifdef S_ISLNK + if (symlink(hp->linkName, name) < 0) + perror(name); +#else + fprintf(stderr, "Cannot create symbolic links\n"); +#endif + return; + } + + /* + * If the file is a directory, then just create the path. + */ + if (S_ISDIR(mode)) + { + createPath(name, mode); + + return; + } + + /* + * There is a file to write. + * First create the path to it if necessary with a default permission. + */ + createPath(name, 0777); + + inHeader = (size == 0); + dataCc = size; + + /* + * Start the output file. + */ + if (tostdoutFlag==TRUE) + outFd = STDOUT; + else + outFd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); + + if (outFd < 0) + { + perror(name); + skipFileFlag = TRUE; + return; + } + + /* + * If the file is empty, then that's all we need to do. + */ + if (size == 0 && tostdoutFlag == FALSE) + { + (void) close(outFd); + outFd = -1; + } +} + + +/* + * Handle a data block of some specified size that was read. + */ +static void +readData(const char * cp, int count) +{ + /* + * Reduce the amount of data left in this file. + * If there is no more data left, then we need to read + * the header again. + */ + dataCc -= count; + + if (dataCc <= 0) + inHeader = TRUE; + + /* + * If we aren't extracting files or this file is being + * skipped then do nothing more. + */ + if (!extractFlag || skipFileFlag) + return; + + /* + * Write the data to the output file. + */ + if (fullWrite(outFd, cp, count) < 0) + { + perror(outName); + if (tostdoutFlag==FALSE) { + (void) close(outFd); + outFd = -1; + } + skipFileFlag = TRUE; + return; + } + + /* + * If the write failed, close the file and disable further + * writes to this file. + */ + if (dataCc <= 0 && tostdoutFlag==FALSE) + { + if (close(outFd)) + perror(outName); + + outFd = -1; + } +} + + +/* + * Write a tar file containing the specified files. + */ +static void +writeTarFile(int fileCount, char ** fileTable) +{ + struct stat statbuf; + + /* + * Make sure there is at least one file specified. + */ + if (fileCount <= 0) + { + fprintf(stderr, "No files specified to be saved\n"); + errorFlag=TRUE; + } + + /* + * Create the tar file for writing. + */ + if ( (tarName==NULL) || !strcmp( tarName, "-") ) { + tostdoutFlag = TRUE; + tarFd = STDOUT; + } + else + tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if (tarFd < 0) + { + perror(tarName); + errorFlag=TRUE; + return; + } + + /* + * Get the device and inode of the tar file for checking later. + */ + if (fstat(tarFd, &statbuf) < 0) + { + perror(tarName); + errorFlag = TRUE; + goto done; + } + + tarDev = statbuf.st_dev; + tarInode = statbuf.st_ino; + + /* + * Append each file name into the archive file. + * Follow symbolic links for these top level file names. + */ + while (!errorFlag && (fileCount-- > 0)) + { + saveFile(*fileTable++, FALSE); + } + + /* + * Now write an empty block of zeroes to end the archive. + */ + writeTarBlock("", 1); + + +done: + /* + * Close the tar file and check for errors if it was opened. + */ + if ( (tostdoutFlag==FALSE) && (tarFd >= 0) && (close(tarFd) < 0)) + perror(tarName); +} + + +/* + * Save one file into the tar file. + * If the file is a directory, then this will recursively save all of + * the files and directories within the directory. The seeLinks + * flag indicates whether or not we want to see symbolic links as + * they really are, instead of blindly following them. + */ +static void +saveFile(const char * fileName, BOOL seeLinks) +{ + int status; + int mode; + struct stat statbuf; + + if (verboseFlag) + printf("a %s\n", fileName); + + /* + * Check that the file name will fit in the header. + */ + if (strlen(fileName) >= TAR_NAME_SIZE) + { + fprintf(stderr, "%s: File name is too long\n", fileName); + + return; + } + + /* + * Find out about the file. + */ +#ifdef S_ISLNK + if (seeLinks) + status = lstat(fileName, &statbuf); + else +#endif + status = stat(fileName, &statbuf); + + if (status < 0) + { + perror(fileName); + + return; + } + + /* + * Make sure we aren't trying to save our file into itself. + */ + if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) + { + fprintf(stderr, "Skipping saving of archive file itself\n"); + + return; + } + + /* + * Check the type of file. + */ + mode = statbuf.st_mode; + + if (S_ISDIR(mode)) + { + saveDirectory(fileName, &statbuf); + + return; + } + + if (S_ISREG(mode)) + { + saveRegularFile(fileName, &statbuf); + + return; + } + + /* + * The file is a strange type of file, ignore it. + */ + fprintf(stderr, "%s: not a directory or regular file\n", fileName); +} + + +/* + * Save a regular file to the tar file. + */ +static void +saveRegularFile(const char * fileName, const struct stat * statbuf) +{ + BOOL sawEof; + int fileFd; + int cc; + int dataCount; + long fullDataCount; + char data[TAR_BLOCK_SIZE * 16]; + + /* + * Open the file for reading. + */ + fileFd = open(fileName, O_RDONLY); + + if (fileFd < 0) + { + perror(fileName); + + return; + } + + /* + * Write out the header for the file. + */ + writeHeader(fileName, statbuf); + + /* + * Write the data blocks of the file. + * We must be careful to write the amount of data that the stat + * buffer indicated, even if the file has changed size. Otherwise + * the tar file will be incorrect. + */ + fullDataCount = statbuf->st_size; + sawEof = FALSE; + + while (fullDataCount > 0) + { + /* + * Get the amount to write this iteration which is + * the minumum of the amount left to write and the + * buffer size. + */ + dataCount = sizeof(data); + + if (dataCount > fullDataCount) + dataCount = (int) fullDataCount; + + /* + * Read the data from the file if we haven't seen the + * end of file yet. + */ + cc = 0; + + if (!sawEof) + { + cc = fullRead(fileFd, data, dataCount); + + if (cc < 0) + { + perror(fileName); + + (void) close(fileFd); + errorFlag = TRUE; + + return; + } + + /* + * If the file ended too soon, complain and set + * a flag so we will zero fill the rest of it. + */ + if (cc < dataCount) + { + fprintf(stderr, + "%s: Short read - zero filling", + fileName); + + sawEof = TRUE; + } + } + + /* + * Zero fill the rest of the data if necessary. + */ + if (cc < dataCount) + memset(data + cc, 0, dataCount - cc); + + /* + * Write the buffer to the TAR file. + */ + writeTarBlock(data, dataCount); + + fullDataCount -= dataCount; + } + + /* + * Close the file. + */ + if ( (tostdoutFlag==FALSE) && close(fileFd) < 0) + fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno)); +} + + +/* + * Save a directory and all of its files to the tar file. + */ +static void +saveDirectory(const char * dirName, const struct stat * statbuf) +{ + DIR * dir; + struct dirent * entry; + BOOL needSlash; + char fullName[PATH_LEN]; + + /* + * Construct the directory name as used in the tar file by appending + * a slash character to it. + */ + strcpy(fullName, dirName); + strcat(fullName, "/"); + + /* + * Write out the header for the directory entry. + */ + writeHeader(fullName, statbuf); + + /* + * Open the directory. + */ + dir = opendir(dirName); + + if (dir == NULL) + { + fprintf(stderr, "Cannot read directory \"%s\": %s\n", + dirName, strerror(errno)); + + return; + } + + /* + * See if a slash is needed. + */ + needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/')); + + /* + * Read all of the directory entries and check them, + * except for the current and parent directory entries. + */ + while (!errorFlag && ((entry = readdir(dir)) != NULL)) + { + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) + { + continue; + } + + /* + * Build the full path name to the file. + */ + strcpy(fullName, dirName); + + if (needSlash) + strcat(fullName, "/"); + + strcat(fullName, entry->d_name); + + /* + * Write this file to the tar file, noticing whether or not + * the file is a symbolic link. + */ + saveFile(fullName, TRUE); + } + + /* + * All done, close the directory. + */ + closedir(dir); +} + + +/* + * Write a tar header for the specified file name and status. + * It is assumed that the file name fits. + */ +static void +writeHeader(const char * fileName, const struct stat * statbuf) +{ + long checkSum; + const unsigned char * cp; + int len; + TarHeader header; + + /* + * Zero the header block in preparation for filling it in. + */ + memset((char *) &header, 0, sizeof(header)); + + /* + * Fill in the header. + */ + strcpy(header.name, fileName); + + strncpy(header.magic, TAR_MAGIC, sizeof(header.magic)); + strncpy(header.version, TAR_VERSION, sizeof(header.version)); + + putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777); + putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); + putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); + putOctal(header.size, sizeof(header.size), statbuf->st_size); + putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); + + header.typeFlag = TAR_TYPE_REGULAR; + + /* + * Calculate and store the checksum. + * This is the sum of all of the bytes of the header, + * with the checksum field itself treated as blanks. + */ + memset(header.checkSum, ' ', sizeof(header.checkSum)); + + cp = (const unsigned char *) &header; + len = sizeof(header); + checkSum = 0; + + while (len-- > 0) + checkSum += *cp++; + + putOctal(header.checkSum, sizeof(header.checkSum), checkSum); + + /* + * Write the tar header. + */ + writeTarBlock((const char *) &header, sizeof(header)); +} + + +/* + * Write data to one or more blocks of the tar file. + * The data is always padded out to a multiple of TAR_BLOCK_SIZE. + * The errorFlag static variable is set on an error. + */ +static void +writeTarBlock(const char * buf, int len) +{ + int partialLength; + int completeLength; + char fullBlock[TAR_BLOCK_SIZE]; + + /* + * If we had a write error before, then do nothing more. + */ + if (errorFlag) + return; + + /* + * Get the amount of complete and partial blocks. + */ + partialLength = len % TAR_BLOCK_SIZE; + completeLength = len - partialLength; + + /* + * Write all of the complete blocks. + */ + if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength)) + { + perror(tarName); + + errorFlag = TRUE; + + return; + } + + /* + * If there are no partial blocks left, we are done. + */ + if (partialLength == 0) + return; + + /* + * Copy the partial data into a complete block, and pad the rest + * of it with zeroes. + */ + memcpy(fullBlock, buf + completeLength, partialLength); + memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength); + + /* + * Write the last complete block. + */ + if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE)) + { + perror(tarName); + + errorFlag = TRUE; + } +} + + +/* + * Attempt to create the directories along the specified path, except for + * the final component. The mode is given for the final directory only, + * while all previous ones get default protections. Errors are not reported + * here, as failures to restore files can be reported later. + */ +static void +createPath(const char * name, int mode) +{ + char * cp; + char * cpOld; + char buf[TAR_NAME_SIZE]; + + strcpy(buf, name); + + cp = strchr(buf, '/'); + + while (cp) + { + cpOld = cp; + cp = strchr(cp + 1, '/'); + + *cpOld = '\0'; + + if (mkdir(buf, cp ? 0777 : mode) == 0) + printf("Directory \"%s\" created\n", buf); + + *cpOld = '/'; + } +} + + +/* + * Read an octal value in a field of the specified width, with optional + * spaces on both sides of the number and with an optional null character + * at the end. Returns -1 on an illegal format. + */ +static long +getOctal(const char * cp, int len) +{ + long val; + + while ((len > 0) && (*cp == ' ')) + { + cp++; + len--; + } + + if ((len == 0) || !isOctal(*cp)) + return -1; + + val = 0; + + while ((len > 0) && isOctal(*cp)) + { + val = val * 8 + *cp++ - '0'; + len--; + } + + while ((len > 0) && (*cp == ' ')) + { + cp++; + len--; + } + + if ((len > 0) && *cp) + return -1; + + return val; +} + + +/* + * Put an octal string into the specified buffer. + * The number is zero and space padded and possibly null padded. + * Returns TRUE if successful. + */ +static BOOL +putOctal(char * cp, int len, long value) +{ + int tempLength; + char * tempString; + char tempBuffer[32]; + + /* + * Create a string of the specified length with an initial space, + * leading zeroes and the octal number, and a trailing null. + */ + tempString = tempBuffer; + + sprintf(tempString, " %0*lo", len - 2, value); + + tempLength = strlen(tempString) + 1; + + /* + * If the string is too large, suppress the leading space. + */ + if (tempLength > len) + { + tempLength--; + tempString++; + } + + /* + * If the string is still too large, suppress the trailing null. + */ + if (tempLength > len) + tempLength--; + + /* + * If the string is still too large, fail. + */ + if (tempLength > len) + return FALSE; + + /* + * Copy the string to the field. + */ + memcpy(cp, tempString, len); + + return TRUE; +} + + +/* + * See if the specified file name belongs to one of the specified list + * of path prefixes. An empty list implies that all files are wanted. + * Returns TRUE if the file is selected. + */ +static BOOL +wantFileName(const char * fileName, int fileCount, char ** fileTable) +{ + const char * pathName; + int fileLength; + int pathLength; + + /* + * If there are no files in the list, then the file is wanted. + */ + if (fileCount == 0) + return TRUE; + + fileLength = strlen(fileName); + + /* + * Check each of the test paths. + */ + while (fileCount-- > 0) + { + pathName = *fileTable++; + + pathLength = strlen(pathName); + + if (fileLength < pathLength) + continue; + + if (memcmp(fileName, pathName, pathLength) != 0) + continue; + + if ((fileLength == pathLength) || + (fileName[pathLength] == '/')) + { + return TRUE; + } + } + + return FALSE; +} + + + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char * +modeString(int mode) +{ + static char buf[12]; + + strcpy(buf, "----------"); + + /* + * Fill in the file type. + */ + if (S_ISDIR(mode)) + buf[0] = 'd'; + if (S_ISCHR(mode)) + buf[0] = 'c'; + if (S_ISBLK(mode)) + buf[0] = 'b'; + if (S_ISFIFO(mode)) + buf[0] = 'p'; +#ifdef S_ISLNK + if (S_ISLNK(mode)) + buf[0] = 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + buf[0] = 's'; +#endif + + /* + * Now fill in the normal file permissions. + */ + if (mode & S_IRUSR) + buf[1] = 'r'; + if (mode & S_IWUSR) + buf[2] = 'w'; + if (mode & S_IXUSR) + buf[3] = 'x'; + if (mode & S_IRGRP) + buf[4] = 'r'; + if (mode & S_IWGRP) + buf[5] = 'w'; + if (mode & S_IXGRP) + buf[6] = 'x'; + if (mode & S_IROTH) + buf[7] = 'r'; + if (mode & S_IWOTH) + buf[8] = 'w'; + if (mode & S_IXOTH) + buf[9] = 'x'; + + /* + * Finally fill in magic stuff like suid and sticky text. + */ + if (mode & S_ISUID) + buf[3] = ((mode & S_IXUSR) ? 's' : 'S'); + if (mode & S_ISGID) + buf[6] = ((mode & S_IXGRP) ? 's' : 'S'); + if (mode & S_ISVTX) + buf[9] = ((mode & S_IXOTH) ? 't' : 'T'); + + return buf; +} + + +/* + * Get the time string to be used for a file. + * This is down to the minute for new files, but only the date for old files. + * The string is returned from a static buffer, and so is overwritten for + * each call. + */ +const char * +timeString(time_t timeVal) +{ + time_t now; + char * str; + static char buf[26]; + + time(&now); + + str = ctime(&timeVal); + + strcpy(buf, &str[4]); + buf[12] = '\0'; + + if ((timeVal > now) || (timeVal < now - 365*24*60*60L)) + { + strcpy(&buf[7], &str[20]); + buf[11] = '\0'; + } + + return buf; +} + + + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +int +fullWrite(int fd, const char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = write(fd, buf, len); + + if (cc < 0) + return -1; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +fullRead(int fd, char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = read(fd, buf, len); + + if (cc < 0) + return -1; + + if (cc == 0) + break; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + + +#endif +/* END CODE */ + + diff --git a/touch.c b/touch.c new file mode 100644 index 000000000..ca4b98108 --- /dev/null +++ b/touch.c @@ -0,0 +1,20 @@ +#include "internal.h" +#include +#include +#include + +const char touch_usage[] = "touch [-c] file [file ...]\n" +"\n" +"\tUpdate the last-modified date on the given file[s].\n"; + +extern int +touch_fn(const struct FileInfo * i) +{ + if ( (utime(i->source, 0) != 0) && (i->create != 1) ) { + if ( fopen(i->source, "w") == NULL ) { + name_and_error(i->source); + return 1; + } + } + return 0; +} diff --git a/umount.c b/umount.c new file mode 100644 index 000000000..4efc9f9d9 --- /dev/null +++ b/umount.c @@ -0,0 +1,135 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + +const char umount_usage[] = "umount {filesystem|directory}\n" +"\tumount -a\n" +"\n" +"\tUnmount a filesystem.\n" +"\t-a:\tUnmounts all mounted filesystems.\n"; + +static char * +stralloc(const char * string) +{ + int length = strlen(string) + 1; + char * n = malloc(length); + memcpy(n, string, length); + return n; +} + +extern void +erase_mtab(const char * name) +{ + struct mntent entries[100]; + int count = 0; + FILE * mountTable = setmntent("/etc/mtab", "r"); + struct mntent * m; + + if ( mountTable == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 ) { + name_and_error("/etc/mtab"); + return; + } + + while ( (m = getmntent(mountTable)) != 0 ) { + entries[count].mnt_fsname = stralloc(m->mnt_fsname); + entries[count].mnt_dir = stralloc(m->mnt_dir); + entries[count].mnt_type = stralloc(m->mnt_type); + entries[count].mnt_opts = stralloc(m->mnt_opts); + entries[count].mnt_freq = m->mnt_freq; + entries[count].mnt_passno = m->mnt_passno; + count++; + } + endmntent(mountTable); + if ( (mountTable = setmntent("/etc/mtab", "w")) ) { + int i; + for ( i = 0; i < count; i++ ) { + int result = ( strcmp(entries[i].mnt_fsname, name) == 0 + || strcmp(entries[i].mnt_dir, name) == 0 ); + + if ( result ) + continue; + else + addmntent(mountTable, &entries[i]); + } + endmntent(mountTable); + } + else if ( errno != EROFS ) + name_and_error("/etc/mtab"); +} + +static int +umount_all(int noMtab) +{ + struct mntent entries[100]; + int count = 0; + FILE * mountTable = setmntent("/etc/mtab", "r"); + struct mntent * m; + int status = 0; + + if ( mountTable == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 ) { + name_and_error("/etc/mtab"); + return 1; + } + + while ( (m = getmntent(mountTable)) != 0 ) { + entries[count].mnt_fsname = stralloc(m->mnt_fsname); + count++; + } + endmntent(mountTable); + + while ( count > 0 ) { + int result = umount(entries[--count].mnt_fsname) == 0; + /* free(entries[count].mnt_fsname); */ + if ( result ) { + if ( !noMtab ) + erase_mtab(entries[count].mnt_fsname); + } + else { + status = 1; + name_and_error(entries[count].mnt_fsname); + } + } + return status; +} + +extern int +do_umount(const char * name, int noMtab) +{ + if ( umount(name) == 0 ) { + if ( !noMtab ) + erase_mtab(name); + return 0; + } + return 1; +} + +extern int +umount_main(struct FileInfo * i, int argc, char * * argv) +{ + int noMtab = 0; + + if ( argv[1][0] == '-' ) { + switch ( argv[1][1] ) { + case 'a': + return umount_all(noMtab); + case 'n': + noMtab = 1; + break; + default: + usage(umount_usage); + return 1; + } + } + if ( do_umount(argv[1],noMtab) != 0 ) { + fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno)); + return 1; + } + return 0; +} diff --git a/update.c b/update.c new file mode 100644 index 000000000..f3b7fc0c8 --- /dev/null +++ b/update.c @@ -0,0 +1,48 @@ +#include "internal.h" +#include + +const char update_usage[] = "update\n" +"\n" +"\tFlush buffered data to the disk devices every 30 seconds.\n"; + +#if defined(__GLIBC__) +#include +#else +_syscall2(int, bdflush, int, func, int, data); +#endif /* __GLIBC__ */ + +extern int +update_main(struct FileInfo * i, int argc, char * * argv) +{ + /* + * Update is actually two daemons, bdflush and update. + */ + int pid; + + pid = fork(); + if ( pid < 0 ) + return pid; + else if ( pid == 0 ) { + /* + * This is no longer necessary since 1.3.5x, but it will harmlessly + * exit if that is the case. + */ + strcpy(argv[0], "bdflush (update)"); + argv[1] = 0; + argv[2] = 0; + bdflush(1, 0); + _exit(0); + } + pid = fork(); + if ( pid < 0 ) + return pid; + else if ( pid == 0 ) { + argv[0] = "update"; + for ( ; ; ) { + sync(); + sleep(30); + } + } + + return 0; +} diff --git a/util-linux/dmesg.c b/util-linux/dmesg.c new file mode 100644 index 000000000..a63fa3d39 --- /dev/null +++ b/util-linux/dmesg.c @@ -0,0 +1,95 @@ +#include "internal.h" +#include +#include +#include + +/* dmesg.c -- Print out the contents of the kernel ring buffer + * Created: Sat Oct 9 16:19:47 1993 + * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu + * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu) + * This program comes with ABSOLUTELY NO WARRANTY. + * Modifications by Rick Sladkey (jrs@world.std.com) + * from util-linux; adapted for busybox + */ + +#include +#include +#include + +#define __NR_klog __NR_syslog + +#if defined(__GLIBC__) +#include +#define klog klogctl +#else +static inline _syscall3(int,klog,int,type,char *,b,int,len) +#endif /* __GLIBC__ */ + +const char dmesg_usage[] = "dmesg"; + +int +dmesg_main(struct FileInfo * info, int argc, char * * argv) +{ + + char buf[4096]; + int i; + int n; + int c; + int level = 0; + int lastc; + int cmd = 3; + + while ((c = getopt( argc, argv, "cn:" )) != EOF) { + switch (c) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + level = atoi(optarg); + break; + case '?': + default: + usage(dmesg_usage); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) { + usage(dmesg_usage); + exit(1); + } + + if (cmd == 8) { + n = klog( cmd, NULL, level ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + exit( 0 ); + } + + n = klog( cmd, buf, sizeof( buf ) ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + + lastc = '\n'; + for (i = 0; i < n; i++) { + if ((i == 0 || buf[i - 1] == '\n') && buf[i] == '<') { + i++; + while (buf[i] >= '0' && buf[i] <= '9') + i++; + if (buf[i] == '>') + i++; + } + lastc = buf[i]; + putchar( lastc ); + } + if (lastc != '\n') + putchar( '\n' ); + return 0; +} diff --git a/util-linux/fdflush.c b/util-linux/fdflush.c new file mode 100644 index 000000000..a15e9b3f7 --- /dev/null +++ b/util-linux/fdflush.c @@ -0,0 +1,36 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +const char fdflush_usage[] = "fdflush device"; + +int +fdflush(const char *filename) +{ + int status; + int fd = open(filename, 0); + + if ( fd < 0 ) { + name_and_error(filename); + return 1; + } + + status = ioctl(fd, FDFLUSH, 0); + close(fd); + + if ( status != 0 ) { + name_and_error(filename); + return 1; + } + return 0; +} + + +int +fdflush_fn(const struct FileInfo * i) +{ + return fdflush(i->source); +} diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c new file mode 100644 index 000000000..f797d1395 --- /dev/null +++ b/util-linux/mkswap.c @@ -0,0 +1,253 @@ +#include "internal.h" +/* + * mkswap.c - set up a linux swap device + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * 20.12.91 - time began. Got VM working yesterday by doing this by hand. + * + * Usage: mkswap [-c] device [size-in-blocks] + * + * -c for readablility checking (use it unless you are SURE!) + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the + * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef __linux__ +# define volatile +#endif + +#define TEST_BUFFER_PAGES 8 + +const char mkswap_usage[] = "mkswap [-c] partition [block-count]\n" +"\n" +"\tPrepare a disk partition to be used as a swap partition.\n" +"\tThe default block count is the size of the entire partition.\n" +"\n" +"\t-c:\tCheck for read-ability.\n" +"\tblock-count\tUse only this many blocks.\n"; + +static const char * program_name = "mkswap"; +static const char * device_name = NULL; +static int DEV = -1; +static long PAGES = 0; +static int do_check = 0; +static int badpages = 0; + + +static long bit_test_and_set (unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + *addr = r | m; + return (r & m) != 0; +} + +static int bit_test_and_clear (unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + *addr = r & ~m; + return (r & m) != 0; +} + +/* + * Volatile to let gcc know that this doesn't return. When trying + * to compile this under minix, volatile gives a warning, as + * exit() isn't defined as volatile under minix. + */ +volatile void fatal_error(const char * fmt_string) +{ + fprintf(stderr,fmt_string,program_name,device_name); + exit(1); +} + +#define die(str) fatal_error("%s: " str "\n") + +static void check_blocks(int * signature_page) +{ + unsigned int current_page; + int do_seek = 1; + char buffer[PAGE_SIZE]; + + current_page = 0; + while (current_page < PAGES) { + if (!do_check) { + bit_test_and_set(signature_page,current_page++); + continue; + } else { + printf("\r%d", current_page); + } + if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != + current_page*PAGE_SIZE) + die("seek failed in check_blocks"); + if ( (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) ) { + bit_test_and_clear(signature_page,current_page++); + badpages++; + continue; + } + bit_test_and_set(signature_page,current_page++); + } + if (do_check) + printf("\n"); + if (badpages) + printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); +} + +static long valid_offset (int fd, int offset) +{ + char ch; + + if (lseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +static int count_blocks (int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const int mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + return (low + 1); +} + +static int get_size(const char *file) +{ + int fd; + int size; + + fd = open(file, O_RDWR); + if (fd < 0) { + perror(file); + exit(1); + } + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + close(fd); + return (size * 512); + } + + size = count_blocks(fd); + close(fd); + return size; +} + +int +mkswap(char *device_name, int pages, int check) + { + struct stat statbuf; + int goodpages; + int signature_page[PAGE_SIZE/sizeof(int)]; + + PAGES = pages; + do_check = check; + + memset(signature_page,0,PAGE_SIZE); + + if (device_name && !PAGES) { + PAGES = get_size(device_name) / PAGE_SIZE; + } + if (!device_name || PAGES<10) { + fprintf(stderr, + "%s: error: swap area needs to be at least %ldkB\n", + program_name, 10 * PAGE_SIZE / 1024); + /* usage(mkswap_usage); */ + exit(1); + } + if (PAGES > 8 * (PAGE_SIZE - 10)) { + PAGES = 8 * (PAGE_SIZE - 10); + fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n", + program_name, PAGES * PAGE_SIZE / 1024); + } + DEV = open(device_name,O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) { + perror(device_name); + exit(1); + } + if (!S_ISBLK(statbuf.st_mode)) + do_check=0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + die("Will not try to make swapdevice on '%s'"); + check_blocks(signature_page); + if (!bit_test_and_clear(signature_page,0)) + die("fatal: first page unreadable"); + goodpages = PAGES - badpages - 1; + if (goodpages <= 0) + die("Unable to set up swap-space: unreadable"); + printf("Setting up swapspace, size = %ld bytes\n",goodpages*PAGE_SIZE); + strncpy((char*)signature_page+PAGE_SIZE-10,"SWAP-SPACE",10); + if (lseek(DEV, 0, SEEK_SET)) + die("unable to rewind swap-device"); + if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE)) + die("unable to write signature page"); + + close(DEV); + return 0; +} + +int mkswap_main(struct FileInfo * unnecessary, int argc, char ** argv) +{ + char * tmp; + long int pages=0; + int check=0; + + if (argc && *argv) + program_name = *argv; + while (argc > 1) { + argv++; + argc--; + if (argv[0][0] != '-') + if (device_name) { + pages = strtol(argv[0],&tmp,0)>>(PAGE_SHIFT-10); + if (*tmp) { + usage(mkswap_usage); + exit(1); + } + } else + device_name = argv[0]; + else while (*++argv[0]) + switch (argv[0][0]) { + case 'c': check=1; break; + default: usage(mkswap_usage); + exit(1); + } + } + return mkswap(device_name, pages, check); +} diff --git a/util-linux/more.c b/util-linux/more.c new file mode 100644 index 000000000..65409999b --- /dev/null +++ b/util-linux/more.c @@ -0,0 +1,110 @@ +#include "internal.h" +#include +#include +#include +#include +#include + +#define BB_MORE_TERM + +#ifdef BB_MORE_TERM + #include + #include + + FILE *cin; + struct termios initial_settings, new_settings; + + void gotsig(int sig) { + tcsetattr(fileno(cin), TCSANOW, &initial_settings); + exit(0); + } +#endif + +const char more_usage[] = "more [file]\n" +"\n" +"\tDisplays a file, one page at a time.\n" +"\tIf there are no arguments, the standard input is displayed.\n"; + +extern int +more_fn(const struct FileInfo * i) +{ + FILE * f = stdin; + int c; + int lines = 0, tlines = 0; + int next_page = 0; + int rows = 24, cols = 79; +#ifdef BB_MORE_TERM + long sizeb = 0; + struct stat st; + struct winsize win; +#endif + + if ( i ) { + if (! (f = fopen(i->source, "r") )) { + name_and_error(i->source); + return 1; + } + fstat(fileno(f), &st); + sizeb = st.st_size / 100; + } + +#ifdef BB_MORE_TERM + cin = fopen("/dev/tty", "r"); + tcgetattr(fileno(cin),&initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; + tcsetattr(fileno(cin), TCSANOW, &new_settings); + + (void) signal(SIGINT, gotsig); + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &win); + if (win.ws_row > 4) rows = win.ws_row - 2; + if (win.ws_col > 0) cols = win.ws_col - 1; + + +#endif + + while ( (c = getc(f)) != EOF ) { + if ( next_page ) { + char garbage; + int len; + tlines += lines; + lines = 0; + next_page = 0; //Percentage is based on bytes, not lines. + if ( i && i->source ) //It is not very acurate, but still useful. + len = printf("%s - %%%2ld - line: %d", i->source, (ftell(f) - sizeb - sizeb) / sizeb, tlines); + else + len = printf("line: %d", tlines); + + fflush(stdout); +#ifndef BB_MORE_TERM + read(2, &garbage, 1); +#else + do { + fread(&garbage, 1, 1, cin); + } while ((garbage != ' ') && (garbage != '\n')); + + if (garbage == '\n') { + lines = rows; + tlines -= rows; + } + garbage = 0; + //clear line, since tabs don't overwrite. + while(len-- > 0) putchar('\b'); + while(len++ < cols) putchar(' '); + while(len-- > 0) putchar('\b'); + fflush(stdout); +#endif + } + putchar(c); + if ( c == '\n' && ++lines == (rows + 1) ) + next_page = 1; + } + if ( f != stdin ) + fclose(f); +#ifdef BB_MORE_TERM + gotsig(0); +#endif + return 0; +} diff --git a/util-linux/mount.c b/util-linux/mount.c new file mode 100644 index 000000000..010757d1e --- /dev/null +++ b/util-linux/mount.c @@ -0,0 +1,430 @@ +/* + 3/21/1999 Charles P. Wright + searches through fstab when -a is passed + will try mounting stuff with all fses when passed -t auto + + 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. +*/ + +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include + +const char mount_usage[] = "mount\n" +"\t\tmount [flags] special-device directory\n" +"\n" +"Flags:\n" +"\t-a:\tMount all file systems in fstab.\n" +"\t-f:\t\"Fake\" mount. Add entry to mount table but don't mount it.\n" +"\t-n:\tDon't write a mount table entry.\n" +"\t-o option:\tOne of many filesystem options, listed below.\n" +"\t-r:\tMount the filesystem read-only.\n" +"\t-t filesystem-type:\tSpecify the filesystem type.\n" +"\t-w:\tMount for reading and writing (default).\n" +"\n" +"Options for use with the \"-o\" flag:\n" +"\tasync / sync:\tWrites are asynchronous / synchronous.\n" +"\tdev / nodev:\tAllow use of special device files / disallow them.\n" +"\texec / noexec:\tAllow use of executable files / disallow them.\n" +"\tsuid / nosuid:\tAllow set-user-id-root programs / disallow them.\n" +"\tremount: Re-mount a currently-mounted filesystem, changing its flags.\n" +"\tro / rw: Mount for read-only / read-write.\n" +"\t" +"There are EVEN MORE flags that are specific to each filesystem.\n" +"You'll have to see the written documentation for those.\n"; + +struct mount_options { + const char * name; + unsigned long and; + unsigned long or; +}; + +static const struct mount_options mount_options[] = { + { "async", ~MS_SYNCHRONOUS,0 }, + { "defaults", ~0, 0 }, + { "dev", ~MS_NODEV, 0 }, + { "exec", ~MS_NOEXEC, 0 }, + { "nodev", ~0, MS_NODEV }, + { "noexec", ~0, MS_NOEXEC }, + { "nosuid", ~0, MS_NOSUID }, + { "remount", ~0, MS_REMOUNT }, + { "ro", ~0, MS_RDONLY }, + { "rw", ~MS_RDONLY, 0 }, + { "suid", ~MS_NOSUID, 0 }, + { "sync", ~0, MS_SYNCHRONOUS }, + { 0, 0, 0 } +}; + +static void +show_flags(unsigned long flags, char * buffer) +{ + const struct mount_options * f = mount_options; + while ( f->name ) { + if ( flags & f->and ) { + int length = strlen(f->name); + memcpy(buffer, f->name, length); + buffer += length; + *buffer++ = ','; + *buffer = '\0'; + } + f++; + } +} + +static void +one_option( + char * option +,unsigned long * flags +,char * data) +{ + const struct mount_options * f = mount_options; + + while ( f->name != 0 ) { + if ( strcasecmp(f->name, option) == 0 ) { + *flags &= f->and; + *flags |= f->or; + return; + } + f++; + } + if ( *data ) { + data += strlen(data); + *data++ = ','; + } + strcpy(data, option); +} + +static void +parse_mount_options( + char * options +,unsigned long * flags +,char * data) +{ + while ( *options ) { + char * comma = strchr(options, ','); + if ( comma ) + *comma = '\0'; + one_option(options, flags, data); + if ( comma ) { + *comma = ','; + options = ++comma; + } + else + break; + } +} + +int +mount_one( + char * blockDevice +,char * directory +,char * filesystemType +,unsigned long flags +,char * string_flags +,int noMtab +,int fake) +{ + int error = 0; + int status = 0; + + char buf[255]; + + if (!fake) { + if (*filesystemType == 'a') { //Will fail on real FS starting with 'a' + + FILE *f = fopen("/proc/filesystems", "r"); + + if (f == NULL) return 1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + filesystemType = buf; + if (*filesystemType == '\t') { // Not a nodev filesystem + + while (*filesystemType && *filesystemType != '\n') filesystemType++; + *filesystemType = '\0'; + + filesystemType = buf; + filesystemType++; //hop past tab + + status = mount(blockDevice, directory, filesystemType, + flags|MS_MGC_VAL ,string_flags); + error = errno; + + if (status == 0) break; + } + } + fclose(f); + } else { + + status = mount( blockDevice, directory, filesystemType, + flags|MS_MGC_VAL ,string_flags); + error = errno; + } + } + + if ( status == 0 ) { + char * s = &string_flags[strlen(string_flags)]; + FILE * mountTable; + if ( s != string_flags ) { + *s++ = ','; + show_flags(flags, s); + } + if ( !noMtab && (mountTable = setmntent("/etc/mtab", "a+")) ) { + int length = strlen(directory); + struct mntent m; + + if ( length > 1 && directory[length - 1] == '/' ) + directory[length - 1] = '\0'; + + if ( filesystemType == 0 ) { + struct mntent * p + = findMountPoint(blockDevice, "/proc/mounts"); + + if ( p && p->mnt_type ) + filesystemType = p->mnt_type; + } + m.mnt_fsname = blockDevice; + m.mnt_dir = directory; + m.mnt_type = filesystemType ? filesystemType : "default"; + + if (*string_flags) { + m.mnt_opts = string_flags; + } else { + if ( (flags | MS_RDONLY) == flags ) + m.mnt_opts = "ro"; + else + m.mnt_opts = "rw"; + } + + m.mnt_freq = 0; + m.mnt_passno = 0; + addmntent(mountTable, &m); + endmntent(mountTable); + } + return 0; + } else { + fprintf(stderr, "Mount %s", blockDevice); + if ( filesystemType && *filesystemType ) + fprintf(stderr, " (type %s)", filesystemType); + + fprintf( + stderr + ," on %s: " + ,directory); + + switch ( error ) { + case EPERM: + if (geteuid() == 0) + fprintf( + stderr + ,"mount point %s is not a directory" + ,blockDevice); + else + fprintf( + stderr + ,"must be superuser to use mount"); + break; + case EBUSY: + fprintf( + stderr + ,"%s already mounted or %s busy" + ,blockDevice + ,directory); + break; + case ENOENT: + { + struct stat statbuf; + if ( stat(directory, &statbuf) != 0 ) + fprintf( + stderr + ,"directory %s does not exist" + ,directory); + else if ( stat(blockDevice, &statbuf) != 0 ) + fprintf( + stderr + ,"block device %s does not exist" + ,blockDevice); + else + fprintf( + stderr + ,"%s is not mounted on %s, but the mount table says it is." + ,blockDevice + ,directory); + break; + } + case ENOTDIR: + fprintf( + stderr + ,"%s is not a directory" + ,directory); + break; + case EINVAL: + fprintf( + stderr + ,"wrong filesystem type, or bad superblock on %s" + ,blockDevice); + break; + case EMFILE: + fprintf(stderr, "mount table full"); + break; + case EIO: + fprintf( + stderr + ,"I/O error reading %s" + ,blockDevice); + break; + case ENODEV: + { + FILE * f = fopen("/proc/filesystems", "r"); + + fprintf( + stderr + ,"filesystem type %s not in kernel.\n" + ,filesystemType); + fprintf(stderr, "Do you need to load a module?\n"); + if ( f ) { + char buf[100]; + + fprintf( + stderr + ,"Here are the filesystem types the kernel" + " can mount:\n"); + while ( fgets(buf, sizeof(buf), f) != 0 ) + fprintf(stderr, "\t%s", buf); + fclose(f); + } + break; + } + case ENOTBLK: + fprintf( + stderr + ,"%s is not a block device" + ,blockDevice); + break; + case ENXIO: + fprintf( + stderr + ,"%s is not a valid block device" + ,blockDevice); + break; + default: + fputs(strerror(errno), stderr); + } + putc('\n', stderr); + return -1; + } +} + +extern int +mount_main(struct FileInfo * i, int argc, char * * argv) +{ + char string_flags[1024]; + unsigned long flags = 0; + char * filesystemType = "auto"; + int fake = 0; + int noMtab = 0; + int all = 0; + + *string_flags = '\0'; + + if ( argc == 1 ) { + FILE * mountTable; + if ( (mountTable = setmntent("/etc/mtab", "r")) ) { + struct mntent * m; + while ( (m = getmntent(mountTable)) != 0 ) { + printf( + "%s on %s type %s (%s)\n" + ,m->mnt_fsname + ,m->mnt_dir + ,m->mnt_type + ,m->mnt_opts); + } + endmntent(mountTable); + } + return 0; + } + + while ( argc >= 2 && argv[1][0] == '-' ) { + switch ( argv[1][1] ) { + case 'f': + fake = 1; + break; + case 'n': + noMtab = 1; + break; + case 'o': + if ( argc < 3 ) { + usage(mount_usage); + return 1; + } + parse_mount_options(argv[2], &flags, string_flags); + argc--; + argv++; + break; + case 'r': + flags |= MS_RDONLY; + break; + case 't': + if ( argc < 3 ) { + usage(mount_usage); + return 1; + } + filesystemType = argv[2]; + argc--; + argv++; + break; + case 'v': + break; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'a': + all = 1; + break; + default: + usage(mount_usage); + return 1; + } + argc--; + argv++; + } + + if (all == 1) { + struct mntent *m; + FILE *f = setmntent("/etc/fstab", "r"); + + if (f == NULL) { + return 1; + } + + // FIXME: Combine read routine (make new function) with unmount_all to save space. + + while ((m = getmntent(f)) != NULL) { + // If the file system isn't noauto, and isn't mounted on /, mount it + if ((!strstr(m->mnt_opts, "noauto")) && (m->mnt_dir[1] != '\0') + && !((m->mnt_type[0] == 's') && (m->mnt_type[1] == 'w')) + && !((m->mnt_type[0] == 'n') && (m->mnt_type[1] == 'f'))) { + mount_one(m->mnt_fsname, m->mnt_dir, m->mnt_type, flags, m->mnt_opts, noMtab, fake); + } + } + + endmntent(f); + } else { + if ( argc >= 3 ) { + if ( mount_one( argv[1], argv[2], filesystemType, flags, string_flags, noMtab, fake) == 0 ) + return 0; + else + return 1; + } else { + usage(mount_usage); + return 1; + } + } + return 0; +} diff --git a/util-linux/umount.c b/util-linux/umount.c new file mode 100644 index 000000000..4efc9f9d9 --- /dev/null +++ b/util-linux/umount.c @@ -0,0 +1,135 @@ +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include + +const char umount_usage[] = "umount {filesystem|directory}\n" +"\tumount -a\n" +"\n" +"\tUnmount a filesystem.\n" +"\t-a:\tUnmounts all mounted filesystems.\n"; + +static char * +stralloc(const char * string) +{ + int length = strlen(string) + 1; + char * n = malloc(length); + memcpy(n, string, length); + return n; +} + +extern void +erase_mtab(const char * name) +{ + struct mntent entries[100]; + int count = 0; + FILE * mountTable = setmntent("/etc/mtab", "r"); + struct mntent * m; + + if ( mountTable == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 ) { + name_and_error("/etc/mtab"); + return; + } + + while ( (m = getmntent(mountTable)) != 0 ) { + entries[count].mnt_fsname = stralloc(m->mnt_fsname); + entries[count].mnt_dir = stralloc(m->mnt_dir); + entries[count].mnt_type = stralloc(m->mnt_type); + entries[count].mnt_opts = stralloc(m->mnt_opts); + entries[count].mnt_freq = m->mnt_freq; + entries[count].mnt_passno = m->mnt_passno; + count++; + } + endmntent(mountTable); + if ( (mountTable = setmntent("/etc/mtab", "w")) ) { + int i; + for ( i = 0; i < count; i++ ) { + int result = ( strcmp(entries[i].mnt_fsname, name) == 0 + || strcmp(entries[i].mnt_dir, name) == 0 ); + + if ( result ) + continue; + else + addmntent(mountTable, &entries[i]); + } + endmntent(mountTable); + } + else if ( errno != EROFS ) + name_and_error("/etc/mtab"); +} + +static int +umount_all(int noMtab) +{ + struct mntent entries[100]; + int count = 0; + FILE * mountTable = setmntent("/etc/mtab", "r"); + struct mntent * m; + int status = 0; + + if ( mountTable == 0 + && (mountTable = setmntent("/proc/mounts", "r")) == 0 ) { + name_and_error("/etc/mtab"); + return 1; + } + + while ( (m = getmntent(mountTable)) != 0 ) { + entries[count].mnt_fsname = stralloc(m->mnt_fsname); + count++; + } + endmntent(mountTable); + + while ( count > 0 ) { + int result = umount(entries[--count].mnt_fsname) == 0; + /* free(entries[count].mnt_fsname); */ + if ( result ) { + if ( !noMtab ) + erase_mtab(entries[count].mnt_fsname); + } + else { + status = 1; + name_and_error(entries[count].mnt_fsname); + } + } + return status; +} + +extern int +do_umount(const char * name, int noMtab) +{ + if ( umount(name) == 0 ) { + if ( !noMtab ) + erase_mtab(name); + return 0; + } + return 1; +} + +extern int +umount_main(struct FileInfo * i, int argc, char * * argv) +{ + int noMtab = 0; + + if ( argv[1][0] == '-' ) { + switch ( argv[1][1] ) { + case 'a': + return umount_all(noMtab); + case 'n': + noMtab = 1; + break; + default: + usage(umount_usage); + return 1; + } + } + if ( do_umount(argv[1],noMtab) != 0 ) { + fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno)); + return 1; + } + return 0; +} diff --git a/utility.c b/utility.c new file mode 100644 index 000000000..68259710b --- /dev/null +++ b/utility.c @@ -0,0 +1,1181 @@ +/* + * Utility routines. + * + * Copyright (C) 1998 by Erik Andersen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ + +#include "utility.h" + +#if 0 + +extern char * +join_paths(char * buffer, const char * a, const char * b) +{ + int length = 0; + + if ( a && *a ) { + length = strlen(a); + memcpy(buffer, a, length); + } + if ( b && *b ) { + if ( length > 0 && buffer[length - 1] != '/' ) + buffer[length++] = '/'; + if ( *b == '/' ) + b++; + strcpy(&buffer[length], b); + } + return buffer; +} + +#endif + + + + + + +static CHUNK * chunkList; + +extern void +name_and_error(const char * name) +{ + fprintf(stderr, "%s: %s\n", name, strerror(errno)); +} + + + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char * +modeString(int mode) +{ + static char buf[12]; + + strcpy(buf, "----------"); + + /* + * Fill in the file type. + */ + if (S_ISDIR(mode)) + buf[0] = 'd'; + if (S_ISCHR(mode)) + buf[0] = 'c'; + if (S_ISBLK(mode)) + buf[0] = 'b'; + if (S_ISFIFO(mode)) + buf[0] = 'p'; +#ifdef S_ISLNK + if (S_ISLNK(mode)) + buf[0] = 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + buf[0] = 's'; +#endif + + /* + * Now fill in the normal file permissions. + */ + if (mode & S_IRUSR) + buf[1] = 'r'; + if (mode & S_IWUSR) + buf[2] = 'w'; + if (mode & S_IXUSR) + buf[3] = 'x'; + if (mode & S_IRGRP) + buf[4] = 'r'; + if (mode & S_IWGRP) + buf[5] = 'w'; + if (mode & S_IXGRP) + buf[6] = 'x'; + if (mode & S_IROTH) + buf[7] = 'r'; + if (mode & S_IWOTH) + buf[8] = 'w'; + if (mode & S_IXOTH) + buf[9] = 'x'; + + /* + * Finally fill in magic stuff like suid and sticky text. + */ + if (mode & S_ISUID) + buf[3] = ((mode & S_IXUSR) ? 's' : 'S'); + if (mode & S_ISGID) + buf[6] = ((mode & S_IXGRP) ? 's' : 'S'); + if (mode & S_ISVTX) + buf[9] = ((mode & S_IXOTH) ? 't' : 'T'); + + return buf; +} + + +/* + * Get the time string to be used for a file. + * This is down to the minute for new files, but only the date for old files. + * The string is returned from a static buffer, and so is overwritten for + * each call. + */ +const char * +timeString(time_t timeVal) +{ + time_t now; + char * str; + static char buf[26]; + + time(&now); + + str = ctime(&timeVal); + + strcpy(buf, &str[4]); + buf[12] = '\0'; + + if ((timeVal > now) || (timeVal < now - 365*24*60*60L)) + { + strcpy(&buf[7], &str[20]); + buf[11] = '\0'; + } + + return buf; +} + + +/* + * Return TRUE if a fileName is a directory. + * Nonexistant files return FALSE. + */ +BOOL +isDirectory(const char * name) +{ + struct stat statBuf; + + if (stat(name, &statBuf) < 0) + return FALSE; + + return S_ISDIR(statBuf.st_mode); +} + + +/* + * Return TRUE if a filename is a block or character device. + * Nonexistant files return FALSE. + */ +BOOL +isDevice(const char * name) +{ + struct stat statBuf; + + if (stat(name, &statBuf) < 0) + return FALSE; + + return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode); +} + + +/* + * Copy one file to another, while possibly preserving its modes, times, + * and modes. Returns TRUE if successful, or FALSE on a failure with an + * error message output. (Failure is not indicted if the attributes cannot + * be set.) + */ +BOOL +copyFile( + const char * srcName, + const char * destName, + BOOL setModes +) +{ + int rfd; + int wfd; + int rcc; + char buf[BUF_SIZE]; + struct stat statBuf1; + struct stat statBuf2; + struct utimbuf times; + + if (stat(srcName, &statBuf1) < 0) + { + perror(srcName); + + return FALSE; + } + + if (stat(destName, &statBuf2) < 0) + { + statBuf2.st_ino = -1; + statBuf2.st_dev = -1; + } + + if ((statBuf1.st_dev == statBuf2.st_dev) && + (statBuf1.st_ino == statBuf2.st_ino)) + { + fprintf(stderr, "Copying file \"%s\" to itself\n", srcName); + + return FALSE; + } + + rfd = open(srcName, O_RDONLY); + + if (rfd < 0) + { + perror(srcName); + + return FALSE; + } + + wfd = creat(destName, statBuf1.st_mode); + + if (wfd < 0) + { + perror(destName); + close(rfd); + + return FALSE; + } + + while ((rcc = read(rfd, buf, sizeof(buf))) > 0) + { + if (fullWrite(wfd, buf, rcc) < 0) + goto error_exit; + } + + if (rcc < 0) + { + perror(srcName); + goto error_exit; + } + + (void) close(rfd); + + if (close(wfd) < 0) + { + perror(destName); + + return FALSE; + } + + if (setModes) + { + (void) chmod(destName, statBuf1.st_mode); + + (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid); + + times.actime = statBuf1.st_atime; + times.modtime = statBuf1.st_mtime; + + (void) utime(destName, ×); + } + + return TRUE; + + +error_exit: + close(rfd); + close(wfd); + + return FALSE; +} + + +/* + * Build a path name from the specified directory name and file name. + * If the directory name is NULL, then the original fileName is returned. + * The built path is in a static area, and is overwritten for each call. + */ +const char * +buildName(const char * dirName, const char * fileName) +{ + const char * cp; + static char buf[PATH_LEN]; + + if ((dirName == NULL) || (*dirName == '\0')) + return fileName; + + cp = strrchr(fileName, '/'); + + if (cp) + fileName = cp + 1; + + strcpy(buf, dirName); + strcat(buf, "/"); + strcat(buf, fileName); + + return buf; +} + + + +/* + * Expand the wildcards in a fileName wildcard pattern, if any. + * Returns an argument list with matching fileNames in sorted order. + * The expanded names are stored in memory chunks which can later all + * be freed at once. The returned list is only valid until the next + * call or until the next command. Returns zero if the name is not a + * wildcard, or returns the count of matched files if the name is a + * wildcard and there was at least one match, or returns -1 if either + * no fileNames matched or there was an allocation error. + */ +int +expandWildCards(const char * fileNamePattern, const char *** retFileTable) +{ + const char * last; + const char * cp1; + const char * cp2; + const char * cp3; + char * str; + DIR * dirp; + struct dirent * dp; + int dirLen; + int newFileTableSize; + char ** newFileTable; + char dirName[PATH_LEN]; + + static int fileCount; + static int fileTableSize; + static char ** fileTable; + + /* + * Clear the return values until we know their final values. + */ + fileCount = 0; + *retFileTable = NULL; + + /* + * Scan the file name pattern for any wildcard characters. + */ + cp1 = strchr(fileNamePattern, '*'); + cp2 = strchr(fileNamePattern, '?'); + cp3 = strchr(fileNamePattern, '['); + + /* + * If there are no wildcard characters then return zero to + * indicate that there was actually no wildcard pattern. + */ + if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL)) + return 0; + + /* + * There are wildcards in the specified filename. + * Get the last component of the file name. + */ + last = strrchr(fileNamePattern, '/'); + + if (last) + last++; + else + last = fileNamePattern; + + /* + * If any wildcards were found before the last filename component + * then return an error. + */ + if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) || + (cp3 && (cp3 < last))) + { + fprintf(stderr, + "Wildcards only implemented for last file name component\n"); + + return -1; + } + + /* + * Assume at first that we are scanning the current directory. + */ + dirName[0] = '.'; + dirName[1] = '\0'; + + /* + * If there was a directory given as part of the file name then + * copy it and null terminate it. + */ + if (last != fileNamePattern) + { + memcpy(dirName, fileNamePattern, last - fileNamePattern); + dirName[last - fileNamePattern - 1] = '\0'; + + if (dirName[0] == '\0') + { + dirName[0] = '/'; + dirName[1] = '\0'; + } + } + + /* + * Open the directory containing the files to be checked. + */ + dirp = opendir(dirName); + + if (dirp == NULL) + { + perror(dirName); + + return -1; + } + + /* + * Prepare the directory name for use in making full path names. + */ + dirLen = strlen(dirName); + + if (last == fileNamePattern) + { + dirLen = 0; + dirName[0] = '\0'; + } + else if (dirName[dirLen - 1] != '/') + { + dirName[dirLen++] = '/'; + dirName[dirLen] = '\0'; + } + + /* + * Find all of the files in the directory and check them against + * the wildcard pattern. + */ + while ((dp = readdir(dirp)) != NULL) + { + /* + * Skip the current and parent directories. + */ + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0)) + { + continue; + } + + /* + * If the file name doesn't match the pattern then skip it. + */ + if (!match(dp->d_name, last)) + continue; + + /* + * This file name is selected. + * See if we need to reallocate the file name table. + */ + if (fileCount >= fileTableSize) + { + /* + * Increment the file table size and reallocate it. + */ + newFileTableSize = fileTableSize + EXPAND_ALLOC; + + newFileTable = (char **) realloc((char *) fileTable, + (newFileTableSize * sizeof(char *))); + + if (newFileTable == NULL) + { + fprintf(stderr, "Cannot allocate file list\n"); + closedir(dirp); + + return -1; + } + + fileTable = newFileTable; + fileTableSize = newFileTableSize; + } + + /* + * Allocate space for storing the file name in a chunk. + */ + str = getChunk(dirLen + strlen(dp->d_name) + 1); + + if (str == NULL) + { + fprintf(stderr, "No memory for file name\n"); + closedir(dirp); + + return -1; + } + + /* + * Save the file name in the chunk. + */ + if (dirLen) + memcpy(str, dirName, dirLen); + + strcpy(str + dirLen, dp->d_name); + + /* + * Save the allocated file name into the file table. + */ + fileTable[fileCount++] = str; + } + + /* + * Close the directory and check for any matches. + */ + closedir(dirp); + + if (fileCount == 0) + { + fprintf(stderr, "No matches\n"); + + return -1; + } + + /* + * Sort the list of file names. + */ + qsort((void *) fileTable, fileCount, sizeof(char *), nameSort); + + /* + * Return the file list and count. + */ + *retFileTable = (const char **) fileTable; + + return fileCount; +} + + +/* + * Sort routine for list of fileNames. + */ +int +nameSort(const void * p1, const void * p2) +{ + const char ** s1; + const char ** s2; + + s1 = (const char **) p1; + s2 = (const char **) p2; + + return strcmp(*s1, *s2); +} + + + +/* + * Routine to see if a text string is matched by a wildcard pattern. + * Returns TRUE if the text is matched, or FALSE if it is not matched + * or if the pattern is invalid. + * * matches zero or more characters + * ? matches a single character + * [abc] matches 'a', 'b' or 'c' + * \c quotes character c + * Adapted from code written by Ingo Wilken. + */ +BOOL +match(const char * text, const char * pattern) +{ + const char * retryPat; + const char * retryText; + int ch; + BOOL found; + + retryPat = NULL; + retryText = NULL; + + while (*text || *pattern) + { + ch = *pattern++; + + switch (ch) + { + case '*': + retryPat = pattern; + retryText = text; + break; + + case '[': + found = FALSE; + + while ((ch = *pattern++) != ']') + { + if (ch == '\\') + ch = *pattern++; + + if (ch == '\0') + return FALSE; + + if (*text == ch) + found = TRUE; + } + + if (!found) + { + pattern = retryPat; + text = ++retryText; + } + + /* fall into next case */ + + case '?': + if (*text++ == '\0') + return FALSE; + + break; + + case '\\': + ch = *pattern++; + + if (ch == '\0') + return FALSE; + + /* fall into next case */ + + default: + if (*text == ch) + { + if (*text) + text++; + break; + } + + if (*text) + { + pattern = retryPat; + text = ++retryText; + break; + } + + return FALSE; + } + + if (pattern == NULL) + return FALSE; + } + + return TRUE; +} + + +/* + * Take a command string and break it up into an argc, argv list while + * handling quoting and wildcards. The returned argument list and + * strings are in static memory, and so are overwritten on each call. + * The argument list is ended with a NULL pointer for convenience. + * Returns TRUE if successful, or FALSE on an error with a message + * already output. + */ +BOOL +makeArgs(const char * cmd, int * retArgc, const char *** retArgv) +{ + const char * argument; + char * cp; + char * cpOut; + char * newStrings; + const char ** fileTable; + const char ** newArgTable; + int newArgTableSize; + int fileCount; + int len; + int ch; + int quote; + BOOL quotedWildCards; + BOOL unquotedWildCards; + + static int stringsLength; + static char * strings; + static int argCount; + static int argTableSize; + static const char ** argTable; + + /* + * Clear the returned values until we know them. + */ + argCount = 0; + *retArgc = 0; + *retArgv = NULL; + + /* + * Copy the command string into a buffer that we can modify, + * reallocating it if necessary. + */ + len = strlen(cmd) + 1; + + if (len > stringsLength) + { + newStrings = realloc(strings, len); + + if (newStrings == NULL) + { + fprintf(stderr, "Cannot allocate string\n"); + + return FALSE; + } + + strings = newStrings; + stringsLength = len; + } + + memcpy(strings, cmd, len); + cp = strings; + + /* + * Keep parsing the command string as long as there are any + * arguments left. + */ + while (*cp) + { + /* + * Save the beginning of this argument. + */ + argument = cp; + cpOut = cp; + + /* + * Reset quoting and wildcarding for this argument. + */ + quote = '\0'; + quotedWildCards = FALSE; + unquotedWildCards = FALSE; + + /* + * Loop over the string collecting the next argument while + * looking for quoted strings or quoted characters, and + * remembering whether there are any wildcard characters + * in the argument. + */ + while (*cp) + { + ch = *cp++; + + /* + * If we are not in a quote and we see a blank then + * this argument is done. + */ + if (isBlank(ch) && (quote == '\0')) + break; + + /* + * If we see a backslash then accept the next + * character no matter what it is. + */ + if (ch == '\\') + { + ch = *cp++; + + /* + * Make sure there is a next character. + */ + if (ch == '\0') + { + fprintf(stderr, + "Bad quoted character\n"); + + return FALSE; + } + + /* + * Remember whether the quoted character + * is a wildcard. + */ + if (isWildCard(ch)) + quotedWildCards = TRUE; + + *cpOut++ = ch; + + continue; + } + + /* + * If we see one of the wildcard characters then + * remember whether it was seen inside or outside + * of quotes. + */ + if (isWildCard(ch)) + { + if (quote) + quotedWildCards = TRUE; + else + unquotedWildCards = TRUE; + } + + /* + * If we were in a quote and we saw the same quote + * character again then the quote is done. + */ + if (ch == quote) + { + quote = '\0'; + + continue; + } + + /* + * If we weren't in a quote and we see either type + * of quote character, then remember that we are + * now inside of a quote. + */ + if ((quote == '\0') && ((ch == '\'') || (ch == '"'))) + { + quote = ch; + + continue; + } + + /* + * Store the character. + */ + *cpOut++ = ch; + } + + /* + * Make sure that quoting is terminated properly. + */ + if (quote) + { + fprintf(stderr, "Unmatched quote character\n"); + + return FALSE; + } + + /* + * Null terminate the argument if it had shrunk, and then + * skip over all blanks to the next argument, nulling them + * out too. + */ + if (cp != cpOut) + *cpOut = '\0'; + + while (isBlank(*cp)) + *cp++ = '\0'; + + /* + * If both quoted and unquoted wildcards were used then + * complain since we don't handle them properly. + */ + if (quotedWildCards && unquotedWildCards) + { + fprintf(stderr, + "Cannot use quoted and unquoted wildcards\n"); + + return FALSE; + } + + /* + * Expand the argument into the matching filenames or accept + * it as is depending on whether there were any unquoted + * wildcard characters in it. + */ + if (unquotedWildCards) + { + /* + * Expand the argument into the matching filenames. + */ + fileCount = expandWildCards(argument, &fileTable); + + /* + * Return an error if the wildcards failed to match. + */ + if (fileCount < 0) + return FALSE; + + if (fileCount == 0) + { + fprintf(stderr, "Wildcard expansion error\n"); + + return FALSE; + } + } + else + { + /* + * Set up to only store the argument itself. + */ + fileTable = &argument; + fileCount = 1; + } + + /* + * Now reallocate the argument table to hold the file name. + */ + if (argCount + fileCount >= argTableSize) + { + newArgTableSize = argCount + fileCount + 1; + + newArgTable = (const char **) realloc(argTable, + (sizeof(const char *) * newArgTableSize)); + + if (newArgTable == NULL) + { + fprintf(stderr, "No memory for arg list\n"); + + return FALSE; + } + + argTable = newArgTable; + argTableSize = newArgTableSize; + } + + /* + * Copy the new arguments to the end of the old ones. + */ + memcpy((void *) &argTable[argCount], (const void *) fileTable, + (sizeof(const char **) * fileCount)); + + /* + * Add to the argument count. + */ + argCount += fileCount; + } + + /* + * Null terminate the argument list and return it. + */ + argTable[argCount] = NULL; + + *retArgc = argCount; + *retArgv = argTable; + + return TRUE; +} + + +/* + * Make a NULL-terminated string out of an argc, argv pair. + * Returns TRUE if successful, or FALSE if the string is too long, + * with an error message given. This does not handle spaces within + * arguments correctly. + */ +BOOL +makeString( + int argc, + const char ** argv, + char * buf, + int bufLen +) +{ + int len; + + while (argc-- > 0) + { + len = strlen(*argv); + + if (len >= bufLen) + { + fprintf(stderr, "Argument string too long\n"); + + return FALSE; + } + + strcpy(buf, *argv++); + + buf += len; + bufLen -= len; + + if (argc) + *buf++ = ' '; + + bufLen--; + } + + *buf = '\0'; + + return TRUE; +} + + +/* + * Allocate a chunk of memory (like malloc). + * The difference, though, is that the memory allocated is put on a + * list of chunks which can be freed all at one time. You CAN NOT free + * an individual chunk. + */ +char * +getChunk(int size) +{ + CHUNK * chunk; + + if (size < CHUNK_INIT_SIZE) + size = CHUNK_INIT_SIZE; + + chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE); + + if (chunk == NULL) + return NULL; + + chunk->next = chunkList; + chunkList = chunk; + + return chunk->data; +} + + +/* + * Duplicate a string value using the chunk allocator. + * The returned string cannot be individually freed, but can only be freed + * with other strings when freeChunks is called. Returns NULL on failure. + */ +char * +chunkstrdup(const char * str) +{ + int len; + char * newStr; + + len = strlen(str) + 1; + newStr = getChunk(len); + + if (newStr) + memcpy(newStr, str, len); + + return newStr; +} + + +/* + * Free all chunks of memory that had been allocated since the last + * call to this routine. + */ +void +freeChunks(void) +{ + CHUNK * chunk; + + while (chunkList) + { + chunk = chunkList; + chunkList = chunk->next; + free((char *) chunk); + } +} + + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +int +fullWrite(int fd, const char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = write(fd, buf, len); + + if (cc < 0) + return -1; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +fullRead(int fd, char * buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) + { + cc = read(fd, buf, len); + + if (cc < 0) + return -1; + + if (cc == 0) + break; + + buf += cc; + total+= cc; + len -= cc; + } + + return total; +} + + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +int +recursive( const char *fileName, BOOL followLinks, const char* pattern, + int (*fileAction)(const char* fileName, const struct stat* statbuf), + int (*dirAction)(const char* fileName, const struct stat* statbuf)) +{ + int status; + struct stat statbuf; + struct dirent* next; + + if (followLinks) + status = stat(fileName, &statbuf); + else + status = lstat(fileName, &statbuf); + + if (status < 0) { + perror(fileName); + return( -1); + } + + if (S_ISREG(statbuf.st_mode)) { + if (match(fileName, pattern)) { + if (fileAction==NULL) + fprintf( stdout, "%s\n", fileName); + else + return(fileAction(fileName, &statbuf)); + } + } + else if (S_ISDIR(statbuf.st_mode)) { + if (dirAction==NULL) { + DIR *dir; + if (! match(fileName, pattern)) + return 1; + dir = opendir(fileName); + if (!dir) { + perror(fileName); + return( -1); + } + while ((next = readdir (dir)) != NULL) { + status = recursive(fileName, followLinks, pattern, fileAction, dirAction); + if (status < 0) { + closedir(dir); + return(status); + } + } + status = closedir (dir); + if (status < 0) { + perror(fileName); + return( -1); + } + } + else + return(dirAction(fileName, &statbuf)); + } + return( 1); + +} + + + +/* END CODE */ diff --git a/utility.h b/utility.h new file mode 100644 index 000000000..7b275baa4 --- /dev/null +++ b/utility.h @@ -0,0 +1,73 @@ +/* + * Utility routines. + * + * Copyright (C) 1998 by Erik Andersen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ + +#include "internal.h" +#include +#include +#include +#include +//#include +//#include +#include +#include +#include + +/* + * A chunk of data. + * Chunks contain data which is allocated as needed, but which is + * not freed until all of the data needs freeing, such as at + * the beginning of the next command. + */ +typedef struct chunk CHUNK; +#define CHUNK_INIT_SIZE 4 + +struct chunk { + CHUNK *next; + char data[CHUNK_INIT_SIZE]; /* actually of varying length */ +}; + +const char *modeString(int mode); +const char *timeString(time_t timeVal); +BOOL isDirectory(const char *name); +BOOL isDevice(const char *name); +BOOL copyFile(const char *srcName, const char *destName, BOOL setModes); +const char *buildName(const char *dirName, const char *fileName); +BOOL match(const char *text, const char *pattern); +BOOL makeArgs(const char *cmd, int *retArgc, const char ***retArgv); +BOOL makeString(int argc, const char **argv, char *buf, int bufLen); +char *getChunk(int size); +char *chunkstrdup(const char *str); +void freeChunks(void); +int fullWrite(int fd, const char *buf, int len); +int fullRead(int fd, char *buf, int len); +int +recursive(const char *fileName, BOOL followLinks, const char *pattern, + int (*fileAction) (const char *fileName, + const struct stat * statbuf), + int (*dirAction) (const char *fileName, + const struct stat * statbuf)); + +int nameSort(const void *p1, const void *p2); +int expandWildCards(const char *fileNamePattern, + const char ***retFileTable); diff --git a/zcat.c b/zcat.c new file mode 100644 index 000000000..50fcdfc47 --- /dev/null +++ b/zcat.c @@ -0,0 +1,2262 @@ +/* zcat : stripped version based on gzip sources + Sven Rudolph + */ + +#include "internal.h" + +const char zcat_usage[] = "zcat\n" +"\n" +"\tuncompress gzipped data from stdin to stdout\n"; + + + +/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +#if 0 +static char *license_msg[] = { +" Copyright (C) 1992-1993 Jean-loup Gailly", +" 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, 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.", +0}; +#endif + +/* Compress files with zip algorithm and 'compress' interface. + * See usage() and help() functions below for all options. + * Outputs: + * file.gz: compressed file with same mode, owner, and utimes + * or stdout with -c option or if stdin used as input. + * If the output file name had to be truncated, the original name is kept + * in the compressed file. + * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * + * For the meaning of all compilation flags, see comments in Makefile.in. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include +#include +#include + +/* #include "tailor.h" */ + +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + +/* $Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ + +#define RECORD_IO 0 + +#define get_char() get_byte() +#define put_char(c) put_byte(c) + +/* #include "gzip.h" */ + +/* gzip.h -- common declarations for all gzip modules + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if defined(__STDC__) || defined(PROTO) +# define OF(args) args +#else +# define OF(args) () +#endif + +#ifdef __STDC__ + typedef void *voidp; +#else + typedef char *voidp; +#endif + +/* I don't like nested includes, but the string and io functions are used + * too often + */ +#include +#if !defined(NO_STRING_H) || defined(STDC_HEADERS) +# include +# if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__) +# include +# endif +# define memzero(s, n) memset ((voidp)(s), 0, (n)) +#else +# include +# define strchr index +# define strrchr rindex +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) +# define memzero(s, n) bzero((s), (n)) +#endif + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#define local static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +/* Return codes from gzip */ +#define OK 0 +#define ERROR 1 +#define WARNING 2 + +/* Compression methods (see algorithm.doc) */ +#define DEFLATED 8 + +extern int method; /* compression method */ + +/* To save memory for 16 bit systems, some arrays are overlaid between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * inflate: window inbuf + * unpack: window inbuf prefix_len + * unlzh: left+right window c_table inbuf c_len + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#define SMALL_MEM + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +/*#define DYN_ALLOC*/ + +#ifdef DYN_ALLOC +# define EXTERN(type, array) extern type * array +# define DECLARE(type, array, size) type * array +# define ALLOC(type, array, size) { \ + array = (type*)calloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error("insufficient memory"); \ + } +# define FREE(array) {if (array != NULL) free(array), array=NULL;} +#else +# define EXTERN(type, array) extern type array[] +# define DECLARE(type, array, size) type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +EXTERN(uch, inbuf); /* input buffer */ +EXTERN(uch, outbuf); /* output buffer */ +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ +#define tab_suffix window +#ifndef MAXSEG_64K +# define tab_prefix prev /* hash link (see deflate.c) */ +# define head (prev+WSIZE) /* hash head (see deflate.c) */ + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ +#else +# define tab_prefix0 prev +# define head tab_prefix1 + EXTERN(ush, tab_prefix0); /* prefix for even codes */ + EXTERN(ush, tab_prefix1); /* prefix for odd codes */ +#endif + +extern unsigned insize; /* valid bytes in inbuf */ +extern unsigned inptr; /* index of next byte to be processed in inbuf */ +extern unsigned outcnt; /* bytes in output buffer */ + +extern long bytes_in; /* number of input bytes */ +extern long bytes_out; /* number of output bytes */ +extern long header_bytes;/* number of bytes in gzip header */ + +extern int ifd; /* input file descriptor */ +extern int ofd; /* output file descriptor */ + +extern long ifile_size; /* input file size, -1 for devices (debug only) */ + +typedef int file_t; /* Do not use stdio */ +#define NO_FILE (-1) /* in memory compression */ + + +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +extern int exit_code; /* program exit code */ +extern int verbose; /* be verbose (-v) */ +extern int level; /* compression level */ +extern int test; /* check .z file integrity */ +extern int to_stdout; /* output to stdout (-c) */ +extern int save_orig_name; /* set if original name must be saved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0)) +#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1)) + +/* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} +#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ + flush_window();} + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ if (outcnt < OUTBUFSIZ-2) { \ + outbuf[outcnt++] = (uch) ((w) & 0xff); \ + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ + } else { \ + put_byte((uch)((w) & 0xff)); \ + put_byte((uch)((ush)(w) >> 8)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ + +/* Macros for getting two-byte and four-byte header values */ +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#define WARN(msg) {fprintf msg ; \ + if (exit_code == OK) exit_code = WARNING;} + + /* in unzip.c */ +extern int unzip OF((int in, int out)); + + /* in gzip.c */ +RETSIGTYPE abort_gzip OF((void)); + + /* in deflate.c */ +void lm_init OF((int pack_level, ush *flags)); +ulg deflate OF((void)); + + /* in trees.c */ +void ct_init OF((ush *attr, int *method)); +int ct_tally OF((int dist, int lc)); +ulg flush_block OF((char *buf, ulg stored_len, int eof)); + + /* in bits.c */ +void bi_init OF((file_t zipfile)); +void send_bits OF((int value, int length)); +unsigned bi_reverse OF((unsigned value, int length)); +void bi_windup OF((void)); +void copy_block OF((char *buf, unsigned len, int header)); +extern int (*read_buf) OF((char *buf, unsigned size)); + + /* in util.c: */ +extern int copy OF((int in, int out)); +extern ulg updcrc OF((uch *s, unsigned n)); +extern void clear_bufs OF((void)); +extern int fill_inbuf OF((int eof_ok)); +extern void flush_outbuf OF((void)); +extern void flush_window OF((void)); +extern void write_buf OF((int fd, voidp buf, unsigned cnt)); +#ifndef __linux__ +extern char *basename OF((char *fname)); +#endif /* not __linux__ */ +extern void error OF((char *m)); +extern void warn OF((char *a, char *b)); +extern void read_error OF((void)); +extern void write_error OF((void)); +extern voidp xmalloc OF((unsigned int size)); + + /* in inflate.c */ +extern int inflate OF((void)); + +/* #include "lzw.h" */ + +/* lzw.h -- define the lzw functions. + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if !defined(OF) && defined(lint) +# include "gzip.h" +#endif + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#define BLOCK_MODE 0x80 +/* Block compression: if table is full and compression rate is dropping, + * clear the dictionary. + */ + +#define LZW_RESERVED 0x60 /* reserved bits */ + +#define CLEAR 256 /* flush the dictionary */ +#define FIRST (CLEAR+1) /* first free entry */ + +extern int maxbits; /* max bits per code for LZW */ +extern int block_mode; /* block compress mode -C compatible with 2.0 */ + +extern int lzw OF((int in, int out)); +extern int unlzw OF((int in, int out)); + + +/* #include "revision.h" */ + +/* revision.h -- define the version number + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#define VERSION "1.2.4" +#define PATCHLEVEL 0 +#define REVDATE "18 Aug 93" + +/* This version does not support compression into old compress format: */ +#ifdef LZW +# undef LZW +#endif + +/* $Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $ */ + +/* #include "getopt.h" */ + +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ || defined(PROTO) +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ + + +#include +#include +#include + +#include + +#if defined(DIRENT) +# include + typedef struct dirent dir_type; +# define NLENGTH(dirent) ((int)strlen((dirent)->d_name)) +# define DIR_OPT "DIRENT" +#else +# define NLENGTH(dirent) ((dirent)->d_namlen) +# ifdef SYSDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSDIR" +# else +# ifdef SYSNDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "SYSNDIR" +# else +# ifdef NDIR +# include + typedef struct direct dir_type; +# define DIR_OPT "NDIR" +# else +# define NO_DIR +# define DIR_OPT "NO_DIR" +# endif +# endif +# endif +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +typedef RETSIGTYPE (*sig_type) OF((int)); + +#ifndef O_BINARY +# define O_BINARY 0 /* creation mode for open() */ +#endif + +#ifndef O_CREAT + /* Pure BSD system? */ +# include +# ifndef O_CREAT +# define O_CREAT FCREAT +# endif +# ifndef O_EXCL +# define O_EXCL FEXCL +# endif +#endif + +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif +#define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifdef NO_OFF_T + typedef long off_t; + off_t lseek OF((int fd, off_t offset, int whence)); +#endif + + + /* global buffers */ + +DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA); +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); +DECLARE(ush, d_buf, DIST_BUFSIZE); +DECLARE(uch, window, 2L*WSIZE); +#ifndef MAXSEG_64K + DECLARE(ush, tab_prefix, 1L<= 0) return method; + + if (part_nb == 1) { + fprintf(stderr, "\nnot in gzip format\n"); + exit_code = ERROR; + return -1; + } else { + WARN((stderr, "\ndecompression OK, trailing garbage ignored\n")); + return -2; + } +} + + +/* ======================================================================== + * Free all dynamically allocated variables and exit with the given code. + */ +local void do_exit(exitcode) + int exitcode; +{ + static int in_exit = 0; + + if (in_exit) exit(exitcode); + in_exit = 1; + FREE(inbuf); + FREE(outbuf); + FREE(d_buf); + FREE(window); +#ifndef MAXSEG_64K + FREE(tab_prefix); +#else + FREE(tab_prefix0); + FREE(tab_prefix1); +#endif + exit(exitcode); +} + +/* ======================================================================== + * Signal and error handler. + */ +RETSIGTYPE abort_gzip() +{ + do_exit(ERROR); +} +/* unzip.c -- decompress files in gzip or pkzip format. + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + * + * The code in this file is derived from the file funzip.c written + * and put in the public domain by Mark Adler. + */ + +/* + This version can extract files in gzip or pkzip format. + For the latter, only the first entry is extracted, and it has to be + either deflated or stored. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +/* #include "crypt.h" */ + +/* crypt.h (dummy version) -- do not perform encryption + * Hardly worth copyrighting :-) + */ + +#ifdef CRYPT +# undef CRYPT /* dummy version */ +#endif + +#define RAND_HEAD_LEN 12 /* length of encryption random header */ + +#define zencode +#define zdecode + +/* PKZIP header definitions */ +#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */ +#define LOCFLG 6 /* offset of bit flag */ +#define CRPFLG 1 /* bit for encrypted entry */ +#define EXTFLG 8 /* bit for extended local header */ +#define LOCHOW 8 /* offset of compression method */ +#define LOCTIM 10 /* file mod time (for decryption) */ +#define LOCCRC 14 /* offset of crc */ +#define LOCSIZ 18 /* offset of compressed size */ +#define LOCLEN 22 /* offset of uncompressed length */ +#define LOCFIL 26 /* offset of file name field length */ +#define LOCEXT 28 /* offset of extra field length */ +#define LOCHDR 30 /* size of local header, including sig */ +#define EXTHDR 16 /* size of extended local header, inc sig */ + + +/* Globals */ + +char *key; /* not used--needed to link crypt.c */ +int pkzip = 0; /* set for a pkzip file */ +int ext_header = 0; /* set if extended local header */ + +/* =========================================================================== + * Unzip in to out. This routine works on both gzip and pkzip files. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + */ +int unzip(in, out) + int in, out; /* input and output file descriptors */ +{ + ulg orig_crc = 0; /* original crc */ + ulg orig_len = 0; /* original uncompressed length */ + int n; + uch buf[EXTHDR]; /* extended local header */ + + ifd = in; + ofd = out; + + updcrc(NULL, 0); /* initialize crc */ + + if (pkzip && !ext_header) { /* crc and length at the end otherwise */ + orig_crc = LG(inbuf + LOCCRC); + orig_len = LG(inbuf + LOCLEN); + } + + /* Decompress */ + if (method == DEFLATED) { + + int res = inflate(); + + if (res == 3) { + error("out of memory"); + } else if (res != 0) { + error("invalid compressed data--format violated"); + } + + } else { + error("internal error, invalid method"); + } + + /* Get the crc and original length */ + if (!pkzip) { + /* crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + for (n = 0; n < 8; n++) { + buf[n] = (uch)get_byte(); /* may cause an error if EOF */ + } + orig_crc = LG(buf); + orig_len = LG(buf+4); + + } else if (ext_header) { /* If extended header, check it */ + /* signature - 4bytes: 0x50 0x4b 0x07 0x08 + * CRC-32 value + * compressed size 4-bytes + * uncompressed size 4-bytes + */ + for (n = 0; n < EXTHDR; n++) { + buf[n] = (uch)get_byte(); /* may cause an error if EOF */ + } + orig_crc = LG(buf+4); + orig_len = LG(buf+12); + } + + /* Validate decompression */ + if (orig_crc != updcrc(outbuf, 0)) { + error("invalid compressed data--crc error"); + } + if (orig_len != (ulg)bytes_out) { + error("invalid compressed data--length error"); + } + + /* Check if there are more entries in a pkzip file */ + if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) { + WARN((stderr,"has more than one entry--rest ignored\n")); + } + ext_header = pkzip = 0; /* for next file */ + return OK; +} +/* util.c -- utility functions for gzip support + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifndef NO_FCNTL_H +# include +#endif + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#else + extern int errno; +#endif + +extern ulg crc_32_tab[]; /* crc table, defined below */ + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +ulg updcrc(s, n) + uch *s; /* pointer to bytes to pump through */ + unsigned n; /* number of bytes in s[] */ +{ + register ulg c; /* temporary variable */ + + static ulg crc = (ulg)0xffffffffL; /* shift register contents */ + + if (s == NULL) { + c = 0xffffffffL; + } else { + c = crc; + if (n) do { + c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8); + } while (--n); + } + crc = c; + return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ +} + +/* =========================================================================== + * Clear input and output buffers + */ +void clear_bufs() +{ + outcnt = 0; + insize = inptr = 0; + bytes_in = bytes_out = 0L; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty. + */ +int fill_inbuf(eof_ok) + int eof_ok; /* set if EOF acceptable as a result */ +{ + int len; + + /* Read as much as possible */ + insize = 0; + errno = 0; + do { + len = read(ifd, (char*)inbuf+insize, INBUFSIZ-insize); + if (len == 0 || len == EOF) break; + insize += len; + } while (insize < INBUFSIZ); + + if (insize == 0) { + if (eof_ok) return EOF; + read_error(); + } + bytes_in += (ulg)insize; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output buffer outbuf[0..outcnt-1] and update bytes_out. + * (used for the compressed data only) + */ +void flush_outbuf() +{ + if (outcnt == 0) return; + + write_buf(ofd, (char *)outbuf, outcnt); + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window() +{ + if (outcnt == 0) return; + updcrc(window, outcnt); + + write_buf(ofd, (char *)window, outcnt); + + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +/* =========================================================================== + * Does the same as write(), but also handles partial pipe writes and checks + * for error return. + */ +void write_buf(fd, buf, cnt) + int fd; + voidp buf; + unsigned cnt; +{ + unsigned n; + + while ((n = write(fd, buf, cnt)) != cnt) { + if (n == (unsigned)(-1)) { + write_error(); + } + cnt -= n; + buf = (voidp)((char*)buf+n); + } +} + +#if defined(NO_STRING_H) && !defined(STDC_HEADERS) + +/* Provide missing strspn and strcspn functions. */ + +# ifndef __STDC__ +# define const +# endif + +int strspn OF((const char *s, const char *accept)); +int strcspn OF((const char *s, const char *reject)); + +/* ======================================================================== + * Return the length of the maximum initial segment + * of s which contains only characters in accept. + */ +int strspn(s, accept) + const char *s; + const char *accept; +{ + register const char *p; + register const char *a; + register int count = 0; + + for (p = s; *p != '\0'; ++p) { + for (a = accept; *a != '\0'; ++a) { + if (*p == *a) break; + } + if (*a == '\0') return count; + ++count; + } + return count; +} + +/* ======================================================================== + * Return the length of the maximum inital segment of s + * which contains no characters from reject. + */ +int strcspn(s, reject) + const char *s; + const char *reject; +{ + register int count = 0; + + while (*s != '\0') { + if (strchr(reject, *s++) != NULL) return count; + ++count; + } + return count; +} + +#endif /* NO_STRING_H */ + + +/* ======================================================================== + * Error handlers. + */ +void error(m) + char *m; +{ + fprintf(stderr, "\n%s\n", m); + abort_gzip(); +} + +void warn(a, b) + char *a, *b; /* message strings juxtaposed in output */ +{ + WARN((stderr, "warning: %s%s\n", a, b)); +} + +void read_error() +{ + fprintf(stderr, "\n"); + if (errno != 0) { + perror(""); + } else { + fprintf(stderr, "unexpected end of file\n"); + } + abort_gzip(); +} + +void write_error() +{ + fprintf(stderr, "\n"); + perror(""); + abort_gzip(); +} + + +/* ======================================================================== + * Semi-safe malloc -- never returns NULL. + */ +voidp xmalloc (size) + unsigned size; +{ + voidp cp = (voidp)malloc (size); + + if (cp == NULL) error("out of memory"); + return cp; +} + +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by makecrc.c) + */ +ulg crc_32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +/* inflate.c -- Not copyrighted 1992 by Mark Adler + version c10p1, 10 January 1993 */ + +/* You can do whatever you like with this source file, though I would + prefer that if you modify it and redistribute it that you include + comments to that effect with your name and the date. Thank you. + [The history has been moved to the file ChangeLog.] + */ + +/* + Inflate deflated (PKZIP's method 8 compressed) data. The compression + method searches for as much of the current string of bytes (up to a + length of 258) in the previous 32K bytes. If it doesn't find any + matches (of at least length 3), it codes the next byte. Otherwise, it + codes the length of the matched string and its distance backwards from + the current position. There is a single Huffman code that codes both + single bytes (called "literals") and match lengths. A second Huffman + code codes the distance information, which follows a length code. Each + length or distance code actually represents a base value and a number + of "extra" (sometimes zero) bits to get to add to the base value. At + the end of each deflated block is a special end-of-block (EOB) literal/ + length code. The decoding process is basically: get a literal/length + code; if EOB then done; if a literal, emit the decoded byte; if a + length then get the distance and emit the referred-to bytes from the + sliding window of previously emitted data. + + There are (currently) three kinds of inflate blocks: stored, fixed, and + dynamic. The compressor deals with some chunk of data at a time, and + decides which method to use on a chunk-by-chunk basis. A chunk might + typically be 32K or 64K. If the chunk is uncompressible, then the + "stored" method is used. In this case, the bytes are simply stored as + is, eight bits per byte, with none of the above coding. The bytes are + preceded by a count, since there is no longer an EOB code. + + If the data is compressible, then either the fixed or dynamic methods + are used. In the dynamic method, the compressed data is preceded by + an encoding of the literal/length and distance Huffman codes that are + to be used to decode this block. The representation is itself Huffman + coded, and so is preceded by a description of that code. These code + descriptions take up a little space, and so for small blocks, there is + a predefined set of codes, called the fixed codes. The fixed method is + used if the block codes up smaller that way (usually for quite small + chunks), otherwise the dynamic method is used. In the latter case, the + codes are customized to the probabilities in the current block, and so + can code it much better than the pre-determined fixed codes. + + The Huffman codes themselves are decoded using a mutli-level table + lookup, in order to maximize the speed of decoding plus the speed of + building the decoding tables. See the comments below that precede the + lbits and dbits tuning parameters. + */ + + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarly, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: zcat.c,v 1.1 1999/10/05 16:24:56 andersen Exp $"; +#endif + +#include + +#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H) +# include +#endif + + +#define slide window + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). + Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 + means that v is a literal, 16 < e < 32 means that v is a pointer to + the next table, which codes e - 16 bits, and lastly e == 99 indicates + an unused code. If a code with e == 99 is looked up, this implies an + error in the data. */ +struct huft { + uch e; /* number of extra bits or operation */ + uch b; /* number of bits in this code or subcode */ + union { + ush n; /* literal, length base, or distance base */ + struct huft *t; /* pointer to next level of table */ + } v; +}; + + +/* Function prototypes */ +int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *, + struct huft **, int *)); +int huft_free OF((struct huft *)); +int inflate_codes OF((struct huft *, struct huft *, int, int)); +int inflate_stored OF((void)); +int inflate_fixed OF((void)); +int inflate_dynamic OF((void)); +int inflate_block OF((int *)); +int inflate OF((void)); + + +/* The inflate algorithm uses a sliding 32K byte window on the uncompressed + stream to find repeated byte strings. This is implemented here as a + circular buffer. The index is updated simply by incrementing and then + and'ing with 0x7fff (32K-1). */ +/* It is left to other modules to supply the 32K area. It is assumed + to be usable as if it were declared "uch slide[32768];" or as just + "uch *slide;" and then malloc'ed in the latter case. The definition + must be in unzip.h, included above. */ +/* unsigned wp; current position in slide */ +#define wp outcnt +#define flush_output(w) (wp=(w),flush_window()) + +/* Tables for deflate from PKZIP's appnote.txt. */ +static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; +static ush cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* note: see note #13 above about the 258 in this list. */ +static ush cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ +static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +static ush cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + + +/* Macros for inflate() bit peeking and grabbing. + The usage is: + + NEEDBITS(j) + x = b & mask_bits[j]; + DUMPBITS(j) + + where NEEDBITS makes sure that b has at least j bits in it, and + DUMPBITS removes the bits from b. The macros use the variable k + for the number of bits in b. Normally, b and k are register + variables for speed, and are initialized at the beginning of a + routine that uses these macros from a global bit buffer and count. + + If we assume that EOB will be the longest code, then we will never + ask for bits with NEEDBITS that are beyond the end of the stream. + So, NEEDBITS should not read any more bytes than are needed to + meet the request. Then no bytes need to be "returned" to the buffer + at the end of the last block. + + However, this assumption is not true for fixed blocks--the EOB code + is 7 bits, but the other literal/length codes can be 8 or 9 bits. + (The EOB code is shorter than other codes because fixed blocks are + generally short. So, while a block always has an EOB, many other + literal/length codes have a significantly lower probability of + showing up at all.) However, by making the first table have a + lookup of seven bits, the EOB code will be found in that first + lookup, and so will not require that too many bits be pulled from + the stream. + */ + +ulg bb; /* bit buffer */ +unsigned bk; /* bits in bit buffer */ + +ush mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +#ifdef CRYPT + uch cc; +# define NEXTBYTE() (cc = get_byte(), zdecode(cc), cc) +#else +# define NEXTBYTE() (uch)get_byte() +#endif +#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<>=(n);k-=(n);} + + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +int lbits = 9; /* bits in base literal/length lookup table */ +int dbits = 6; /* bits in base distance lookup table */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +#define BMAX 16 /* maximum bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + + +unsigned hufts; /* track memory usage */ + + +int huft_build(b, n, s, d, e, t, m) +unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ +unsigned n; /* number of codes (assumed <= N_MAX) */ +unsigned s; /* number of simple-valued codes (0..s-1) */ +ush *d; /* list of base values for non-simple codes */ +ush *e; /* list of extra bits for non-simple codes */ +struct huft **t; /* result: starting table */ +int *m; /* maximum lookup bits, returns actual */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. */ +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX+1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ + register struct huft *q; /* points to current table */ + struct huft r; /* table entry for structure assignment */ + struct huft *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX+1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + memzero(c, sizeof(c)); + p = b; i = n; + do { + Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), + n-i, *p)); + c[*p]++; /* assume all entries <= BMAX */ + p++; /* Can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (struct huft *)NULL; + *m = 0; + return 0; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((unsigned)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((unsigned)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= c[i]) < 0) + return 2; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (struct huft *)NULL; /* just to keep compilers happy */ + q = (struct huft *)NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (struct huft *)malloc((z + 1)*sizeof(struct huft))) == + (struct huft *)NULL) + { + if (h) + huft_free(u[0]); + return 3; /* not enough memory */ + } + hufts += z + 1; /* track memory usage */ + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->v.t)) = (struct huft *)NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.b = (uch)l; /* bits to dump before this table */ + r.e = (uch)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (uch)(k - w); + if (p >= v + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = (ush)(*p); /* simple code is just the value */ + p++; /* one compiler does not like *p++ */ + } + else + { + r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + + + +int huft_free(t) +struct huft *t; /* table to free */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register struct huft *p, *q; + + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != (struct huft *)NULL) + { + q = (--p)->v.t; + free((char*)p); + p = q; + } + return 0; +} + + +int inflate_codes(tl, td, bl, bd) +struct huft *tl, *td; /* literal/length and distance decoder tables */ +int bl, bd; /* number of bits decoded by tl[] and td[] */ +/* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ +{ + register unsigned e; /* table entry flag/number of extra bits */ + unsigned n, d; /* length and index for copy */ + unsigned w; /* current window position */ + struct huft *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) /* do until end of block */ + { + NEEDBITS((unsigned)bl) + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + { + slide[w++] = (uch)t->v.n; + Tracevv((stderr, "%c", slide[w-1])); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + break; + + /* get length of block to copy */ + NEEDBITS(e) + n = t->v.n + ((unsigned)b & mask_bits[e]); + DUMPBITS(e); + + /* decode distance of block to copy */ + NEEDBITS((unsigned)bd) + if ((e = (t = td + ((unsigned)b & md))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + NEEDBITS(e) + d = w - t->v.n - ((unsigned)b & mask_bits[e]); + DUMPBITS(e) + Tracevv((stderr,"\\[%d,%d]", w-d, n)); + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) /* (this test assumes unsigned comparison) */ + { + memcpy(slide + w, slide + d, e); + w += e; + d += e; + } + else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + slide[w++] = slide[d++]; + Tracevv((stderr, "%c", slide[w-1])); + } while (--e); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } while (n); + } + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + + + +int inflate_stored() +/* "decompress" an inflated type 0 (stored) block. */ +{ + unsigned n; /* number of bytes in block */ + unsigned w; /* current window position */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + + /* go to byte boundary */ + n = k & 7; + DUMPBITS(n); + + + /* get the length and its complement */ + NEEDBITS(16) + n = ((unsigned)b & 0xffff); + DUMPBITS(16) + NEEDBITS(16) + if (n != (unsigned)((~b) & 0xffff)) + return 1; /* error in compressed data */ + DUMPBITS(16) + + + /* read and output the compressed data */ + while (n--) + { + NEEDBITS(8) + slide[w++] = (uch)b; + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + DUMPBITS(8) + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + return 0; +} + + + +int inflate_fixed() +/* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ +{ + int i; /* temporary variable */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned l[288]; /* length list for huft_build */ + + + /* set up literal table */ + for (i = 0; i < 144; i++) + l[i] = 8; + for (; i < 256; i++) + l[i] = 9; + for (; i < 280; i++) + l[i] = 7; + for (; i < 288; i++) /* make a complete, but wrong code set */ + l[i] = 8; + bl = 7; + if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) + return i; + + + /* set up distance table */ + for (i = 0; i < 30; i++) /* make an incomplete code set */ + l[i] = 5; + bd = 5; + if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) + { + huft_free(tl); + return i; + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +int inflate_dynamic() +/* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ +#ifdef PKZIP_BUG_WORKAROUND + unsigned ll[288+32]; /* literal/length and distance code lengths */ +#else + unsigned ll[286+30]; /* literal/length and distance code lengths */ +#endif + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in table lengths */ + NEEDBITS(5) + nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */ + DUMPBITS(5) + NEEDBITS(5) + nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */ + DUMPBITS(5) + NEEDBITS(4) + nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */ + DUMPBITS(4) +#ifdef PKZIP_BUG_WORKAROUND + if (nl > 288 || nd > 32) +#else + if (nl > 286 || nd > 30) +#endif + return 1; /* bad lengths */ + + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) + { + NEEDBITS(3) + ll[border[j]] = (unsigned)b & 7; + DUMPBITS(3) + } + for (; j < 19; j++) + ll[border[j]] = 0; + + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) + { + if (i == 1) + huft_free(tl); + return i; /* incomplete code set */ + } + + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned)i < n) + { + NEEDBITS((unsigned)bl) + j = (td = tl + ((unsigned)b & m))->b; + DUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + NEEDBITS(2) + j = 3 + ((unsigned)b & 3); + DUMPBITS(2) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + NEEDBITS(3) + j = 3 + ((unsigned)b & 7); + DUMPBITS(3) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + NEEDBITS(7) + j = 11 + ((unsigned)b & 0x7f); + DUMPBITS(7) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + + + /* free decoding table for trees */ + huft_free(tl); + + + /* restore the global bit buffer */ + bb = b; + bk = k; + + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) + { + if (i == 1) { + fprintf(stderr, " incomplete literal tree\n"); + huft_free(tl); + } + return i; /* incomplete code set */ + } + bd = dbits; + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) + { + if (i == 1) { + fprintf(stderr, " incomplete distance tree\n"); +#ifdef PKZIP_BUG_WORKAROUND + i = 0; + } +#else + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ +#endif + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +int inflate_block(e) +int *e; /* last block flag */ +/* decompress an inflated block */ +{ + unsigned t; /* block type */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in last block bit */ + NEEDBITS(1) + *e = (int)b & 1; + DUMPBITS(1) + + + /* read in block type */ + NEEDBITS(2) + t = (unsigned)b & 3; + DUMPBITS(2) + + + /* restore the global bit buffer */ + bb = b; + bk = k; + + + /* inflate that block type */ + if (t == 2) + return inflate_dynamic(); + if (t == 0) + return inflate_stored(); + if (t == 1) + return inflate_fixed(); + + + /* bad block type */ + return 2; +} + + + +int inflate() +/* decompress an inflated entry */ +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h; /* maximum struct huft's malloc'ed */ + + + /* initialize window, bit buffer */ + wp = 0; + bk = 0; + bb = 0; + + + /* decompress until the last block */ + h = 0; + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) + return r; + if (hufts > h) + h = hufts; + } while (!e); + + /* Undo too much lookahead. The next read will be byte aligned so we + * can discard unused bits in the last meaningful byte. + */ + while (bk >= 8) { + bk -= 8; + inptr--; + } + + /* flush out slide */ + flush_output(wp); + + + /* return success */ +#ifdef DEBUG + fprintf(stderr, "<%u> ", h); +#endif /* DEBUG */ + return 0; +}