nick@nswitgould.OZ (Nick Andrew) (04/02/89)
DU 1.4a BUG: Minix 1.4a du.c has a bug where it may incorrectly report the space occupied by a directory if that directory contains filenames of length 14. This bug is hard to reproduce (I have a lot of files called newsXXXX.nws.Z) but annoying. All programs dealing with directory slots should be aware that 14-character filenames are not null terminated. This sort of bug should have been fixed in 1.3 :-( The du.c.cdif file fixes this bug by changing the makedname() routine to copy a maximum of 14 character filenames. MY IMPROVEMENT: Sometimes I like to do a du, and only see the sizes for the topmost directories. For example, on a system where /users contains one directory for each user, and each user may have subdirectories, I only want to see the total amount of space used by each user. This is a fairly trivial change to du. It involves specifying the required number of levels of space to print. Each time dodir() is called recursively, it is passed a decreasing level count. The total line is only printed if the level is 0 or greater. File size lines (-a option) are only printed if the level is greater than 0. The output is intuitive here, as 'du -a -l 2' will print (not in this order though) sizes for the current directory, files in the current directory, files in all sub-directories, all sub-directories, and all sub-sub-directories. I don't expect this change to become part of 1.4b, as it is non standard. But it is useful nevertheless, and transparent to the user. du.c included contains the bugfix as well as the above improvement. Nick Andrew (nick@nswitgould.cs.nswit.oz) --------------------------- hack here -------------------------------------- echo x - du.1 sed '/^X/s///' > du.1 << '/' XCommand: du - print disk usage XSyntax: du [-a] [-s] [-l levels] [directory] XFlags: -a Print sizes of files as well as directories X -s Summary total only X -l levels Print stats for 'levels' deep (levels>=0) XExample: X _D_u examines a directory and prints the amount of space occupied by the Xfiles in that directory and its subdirectories. If 'directory' is not Xspecified, the disk usage for the current directory is printed. If the X'-l levels' parameter is specified, sizes are only printed for the Xspecified number of directory levels (Eg: 'du -l 1' prints the sizes of Ximmediate subdirectories and current directory only; 'du -l 0' is identical Xto 'du -s'). / echo x - du.c.cdif sed '/^X/s///' > du.c.cdif << '/' X*** /minix/commands/du.c Sun Oct 23 22:28:44 1988 X--- du.c Fri Mar 24 17:10:58 1989 X*************** X*** 2,7 **** X--- 2,9 ---- X X /* X * du.c 1.1 27/5/87 agc Joypace Ltd. X+ * 1.2 24 Mar 89 nick@nswitgould.oz X+ * Fixed bug involving 14 character long filenames X * X * Copyright 1987, Joypace Ltd., London UK. All rights reserved. X * This code may be freely distributed, provided that this notice X*************** X*** 112,119 **** X } X X /* X! * makedname - make the direcory entry from the directory name, X! * and the file name, placing it in out. If this would overflow, X * return 0, otherwise 1. X */ X int makedname(d, f, out, outlen) X--- 114,121 ---- X } X X /* X! * makedname - make the pathname from the directory name, and the X! * directory entry, placing it in out. If this would overflow, X * return 0, otherwise 1. X */ X int makedname(d, f, out, outlen) X*************** X*** 123,136 **** X int outlen; X { X char *cp; X! X! if (strlen(d) + strlen(f) + 2 > outlen) X return(0); X for (cp = out ; *d ; *cp++ = *d++) X ; X if (*(cp-1) != '/') X *cp++ = '/'; X! while (*f) X *cp++ = *f++; X *cp = '\0'; X return(1); X--- 125,143 ---- X int outlen; X { X char *cp; X! int length; X! X! /* find length (1-14) of directory entry */ X! cp = f; X! for (length=0; *cp && (length<14); ++cp) ++length; X! X! if (strlen(d) + length + 2 > outlen) X return(0); X for (cp = out ; *d ; *cp++ = *d++) X ; X if (*(cp-1) != '/') X *cp++ = '/'; X! while (length--) X *cp++ = *f++; X *cp = '\0'; X return(1); / echo x - du.c sed '/^X/s///' > du.c << '/' X/* du - report on disk usage Author: Alistair G. Crooks */ X X/* X * du.c 1.1 27/5/87 agc Joypace Ltd. X * 1.2 24 Mar 89 nick@nswitgould.oz X * 1.3 31 Mar 89 nick@nswitgould.oz X * X * Copyright 1987, Joypace Ltd., London UK. All rights reserved. X * This code may be freely distributed, provided that this notice X * remains attached. X * X * du - a public domain interpretation of du(1). X * X * 1.2: Fixed bug involving 14 character long filenames X * 1.3: Add [-l levels] option to restrict printing. X * X */ X X X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <minix/blocksize.h> X Xchar *prog; /* program name */ Xchar *optstr = "asl:"; /* -a and -s arguments */ Xint silent = 0; /* silent mode */ Xint all = 0; /* all directory entries mode */ Xchar *startdir = "."; /* starting from here */ Xint levels = 255; /* # of directory levels to print */ X X#define LINELEN 256 X X#define DIRNAMELEN 14 X#define LSTAT stat Xtypedef struct _dirstr { X int inum; X char d_name[DIRNAMELEN]; X} DIR; XDIR dir; X#define ino_t unsigned short X Xtypedef struct _alstr { X int al_dev; X ino_t al_inum; X} ALREADY; X X#define MAXALREADY 50 XALREADY already[MAXALREADY]; Xint alc = 0; X X/* X * myindex - stop the scanf bug X */ Xchar *myindex(s, c) Xregister char *s; Xregister char c; X{ X for (; *s; s++) X if (*s == c) X return(s); X return(NULL); X} X X X/* X * getopt - parse the arguments given. X * retrieved from net.sources X */ Xint opterr = 1; Xint optind = 1; Xint optopt; Xchar *optarg; X X#define BADCH (int)'?' X#define EMSG "" X#define TELL(s) fputs(*nargv, stderr); fputs(s, stderr);\ X fputc(optopt, stderr); fputc('\n', stderr);\ X return(BADCH); X Xint getopt(nargc, nargv, ostr) Xint nargc; Xchar **nargv; Xchar *ostr; X{ X register char *oli; X static char *place = EMSG; X X if (!*place) { X if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) X return(EOF); X if (*place == '-') { X ++optind; X return(EOF); X } X } X if ((optopt = (int)*place++) == (int)':' || !(oli = myindex(ostr, optopt))) { X if (!*place) X ++optind; X TELL(": illegal option -- "); X } X if (*++oli != ':') { X optarg = NULL; X if (!*place) X ++optind; X } else { X if (*place) X optarg = place; X else if (nargc <= ++optind) { X place = EMSG; X TELL(": option requires an argument -- "); X } else X optarg = nargv[optind]; X place = EMSG; X ++optind; X } X return(optopt); X} X X/* X * makedname - make the pathname from the directory name, and the X * directory entry, placing it in out. If this would overflow, X * return 0, otherwise 1. X */ Xint makedname(d, f, out, outlen) Xchar *d; Xchar *f; Xchar *out; Xint outlen; X{ X char *cp; X int length; X X /* find length (1-14) of directory entry */ X cp = f; X for (length=0; *cp && (length<14); ++cp) ++length; X X if (strlen(d) + length + 2 > outlen) X return(0); X for (cp = out ; *d ; *cp++ = *d++) X ; X if (*(cp-1) != '/') X *cp++ = '/'; X while (length--) X *cp++ = *f++; X *cp = '\0'; X return(1); X} X X/* X * done - have we encountered (dev, inum) before? Returns 1 for yes, X * 0 for no, and remembers (dev, inum). X */ Xint done(dev, inum) Xint dev; Xino_t inum; X{ X register ALREADY *ap; X register int i; X int ret = 0; X X for (i = 0, ap = already ; i < alc ; ap++, i++) X if (ap->al_dev == dev && ap->al_inum == inum) { X ret = 1; X break; X } X if (alc < MAXALREADY) { X already[alc].al_dev = dev; X already[alc++].al_inum = inum; X } X return(ret); X} X X/* X * dodir - process the directory d. Return the long size (in blocks) X * of d and its descendants. X */ Xlong dodir(d,thislev) Xchar *d; Xint thislev; X{ X struct stat s; X long total = 0L; X char dent[LINELEN]; X int fd; X X if ((fd = open(d, 0)) < 0) X return(0L); X while (read(fd, &dir, sizeof(dir)) > 0) { X if (strcmp(dir.d_name, ".") == 0 || X strcmp(dir.d_name, "..") == 0) X continue; X if (dir.inum == 0) X continue; X if (!makedname(d, dir.d_name, dent, sizeof(dent))) X continue; X if (LSTAT(dent, &s) < 0) X continue; X if (s.st_nlink > 1 && done(s.st_dev, s.st_ino)) X continue; X if ((s.st_mode & S_IFMT) == S_IFDIR) X total += dodir(dent,thislev-1); X switch(s.st_mode & S_IFMT) { X case S_IFREG: X case S_IFDIR: X total += (s.st_size + BLOCK_SIZE) / BLOCK_SIZE; X break; X } X if (all && (s.st_mode & S_IFMT) != S_IFDIR) X if (thislev>0) /* this is correct - file in subdir */ X printf("%D\t%s\n", (s.st_size + BLOCK_SIZE) / BLOCK_SIZE, dent); X } X close(fd); X if (!silent) X if (thislev>=0) /* this is correct - subdir itself */ X printf("%D\t%s\n", total, d); X return(total); X} X X/* X * OK, here goes... X */ Xmain(argc, argv) Xint argc; Xchar **argv; X{ X long tot; X int c; X X prog = argv[0]; X while ((c = getopt(argc, argv, optstr)) != EOF) X switch(c) { X case 'a' : X all = 1; X break; X case 's' : X silent = 1; X break; X case 'l': X levels = atoi(optarg); X break; X default : X fprintf(stderr, "Usage: %s [-a] [-s] [-l levels] [startdir]\n", prog); X exit(1); X } X if (optind < argc) X startdir = argv[optind]; X tot = dodir(startdir,levels); X if (silent) X printf("%D\t%s\n", tot, startdir); X exit(0); X} / echo x - README sed '/^X/s///' > README << '/' XManifest: X README This file X du.1 Revised manual page for du X du.c.cdif Cdiff against Minix 1.4a du.c to fix filenames bug X du.c du.c version 1.3 with -l parameter X X"to du or not to du, that is the question" 2du | ~2du => q X XDU 1.4a BUG: X X Minix 1.4a du.c has a bug where it may incorrectly report the space Xoccupied by a directory if that directory contains filenames of length 14. XThis bug is hard to reproduce (I have a lot of files called newsXXXX.nws.Z) Xbut annoying. All programs dealing with directory slots should be aware Xthat 14-character filenames are not null terminated. This sort of bug should Xhave been fixed in 1.3 :-( X X The du.c.cdif file fixes this bug by changing the makedname() Xroutine to copy a maximum of 14 character filenames. X XMY IMPROVEMENT: X X Sometimes I like to do a du, and only see the sizes for the topmost Xdirectories. For example, on a system where /users contains one directory Xfor each user, and each user may have subdirectories, I only want to see Xthe total amount of space used by each user. X X This is a fairly trivial change to du. It involves specifying the Xrequired number of levels of space to print. Each time dodir() is called Xrecursively, it is passed a decreasing level count. The total line is Xonly printed if the level is 0 or greater. File size lines (-a option) Xare only printed if the level is greater than 0. The output is intuitive Xhere, as 'du -a -l 2' will print (not in this order though) sizes for the Xcurrent directory, files in the current directory, files in all Xsub-directories, all sub-directories, and all sub-sub-directories. X X I don't expect this change to become part of 1.4b, as it is non Xstandard. But it is useful nevertheless, and transparent to the user. X X du.c included contains the bugfix as well as the above improvement. / exit 0 -- "Zeta Microcomputer Software" ACSnet: nick@nswitgould.oz UUCP: ...uunet!munnari!nswitgould.oz!nick Fidonet: Nick Andrew on 713/602 (Zeta), nick@nswitgould on 713/603 (ACSgate)