对UNIX V7 ls命令原代码的注释以及移植到现代操作..
时间:2006-04-12 来源:gameboytxt
/*
* 对UNUX V7 ls命令原代码的注释。
* 位置:UNIX V7/usr/src/cmd/ls.c
*/ #include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <stdio.h> #define NFILES 1024
FILE *pwdf, *dirf;
char stdbuf[BUFSIZ];
/* struc lbuf 封装了对ls有用的struct stat */
struct lbuf {
union {
char lname[15];
char *namep; //文件名
} ln;
char ltype; //文件类型
short lnum; //索引结点号
short lflags; //权限位
short lnl; //链接数
short luid; //用户ID
short lgid; //组ID
long lsize; //文件大小
long lmtime; //访问时间
};
/* 每个选项设置一个标记变量 */
int aflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg;
int rflg = 1; //颠倒排序标志 如果使用-r 选项,则rflg会被置-1
long year; //这个变量被设置为从1970年1月1日午夜到当前时间六个月前所经过的秒数
int flags;
int lastuid = -1; //用户ID用于测试
char tbuf[16];
long tblocks; //文件所占用的磁盘块数
int statreq; //使用stat()系统调用的标志,如果为 1 则表示使用stat获取目录信息
struct lbuf *flist[NFILES];
struct lbuf **lastp = flist;
struct lbuf **firstp = flist;//以上三个变量表示用ls 显示信息的文件
char *dotp = "."; //设置默认读取的目录,即当前目录 char *makename(); //连接由斜杠字符分割的目录名和文件名并产生一个字符串
struct lbuf *gstat(); //读取和存储文件信息的核心函数
char *ctime(); //获得时间的函数
long nblock(); //计算文件使用的磁盘块数 #define ISARG 0100000 //用于区分一个文件是由命令行给定还是由目录中读取 main(argc, argv)
char *argv[];
{
int i;
register struct lbuf *ep, **ep1;
register struct lbuf **slastp;
struct lbuf **epp;
struct lbuf lb;
char *t;
int compar(); setbuf(stdout, stdbuf);
time(&lb.lmtime); //获取当前时间
year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 个月之前 */
if (--argc > 0 && *argv[1] == '-') {
argv++; //跳过程序文件名
while (*++*argv) switch (**argv) {//扫描命令行上第一个参数 case 'a': //输出所有目录项。如果没有该选项,则不输出 '.' 和 '..'。
aflg++;
continue; case 's': //输出文件大小。文件名以字母顺序排列
sflg++;
statreq++;
continue; case 'd': //用于目录参数,输出目录本身的信息,而不是目录的内容
dflg++;
continue; case 'g': //用于 'ls -l',但输出组名而不是用户名
gflg++;
continue; case 'l': //在第一列与文件名或长列表一起输出结点索引号
lflg++;
statreq++;
continue; case 'r': //颠倒顺序排列。文件名以字母顺序或时间顺序进行排列
rflg = -1;
continue; case 't': //以修改时间而不是文件名排列输出结果,最近修改的文件处于最前面
tflg++;
statreq++;
continue; case 'u': //以访问时间而不是修改时间排列输出结果,与-t或 -l一起使用
uflg++;
continue; case 'c': //使用索引结点的改变时间,而不是文件的修改时间。该选项与-t或-l一起使用。
cflg++;
continue; case 'i': //在第一列与文件名或长列表一起输出节点索引号
iflg++;
continue; case 'f': //强迫每个参数都当作目录读取,然后输出在每个目录槽中找到的
//文件名。该选项使-l、-r、-s和-t选项无效,但激活了-a选项
fflg++;
continue; default: //忽略未知的选项字母
continue;
}
argc--; //扫描完第一个参数后,由于argv的移动,将argc减1
}
if (fflg) {
aflg++;
lflg = 0;//-f覆盖了-l、-s和-t选项
sflg = 0;
tflg = 0;
statreq = 0;
}
if(lflg) { //打开口令或组文件
t = "/etc/passwd";
if(gflg) //V7 ls命令仅显示用户和组信息中的一项
t = "/etc/group";
pwdf = fopen(t, "r");
}
if (argc==0) {//如果没有指定参数,则使用当前目录
argc++;
argv = &dotp - 1; //减1 是为了对下面的第139行'++argv'进行弥补
}
for (i=0; i < argc; i++) { //获得每个文件信息
if ((ep = gstat(*++argv, 1))==NULL)//gstat的第二个参数表示如果为1则文件
//由命令行参数给定,否则为假
continue;
ep->ln.namep = *argv; //获得文件名
ep->lflags |= ISARG; //加入ISARG位,用于后面判断文件是由命令行给定还是由
//目录读取
}
qsort(firstp, lastp - firstp, sizeof *lastp, compar);//对文件进行排序,这是个
//库中的排序函数
slastp = lastp; //将lastt当前值保存在slastp中
for (epp=firstp; epp<slastp; epp++) {//对数组的每个元素进行循环以适当的方式
//输出文件或目录信息
ep = *epp;
if (ep->ltype=='d' && dflg==0 || fflg) {//如果文件类型为目录并且未提供-d
//选项或提供了-f选项,则ls必须读取该目录而不是输出该目录本身的信息
if (argc>1)//如果命令行提供了多个文件则输出目录名和一个冒号
printf("\n%s:\n", ep->ln.namep);
lastp = slastp; //重设lastp
readdir(ep->ln.namep);
//flist数组作为一个两层的文件名堆栈。命令行参数通过slastp-1保存在flist中,当readdir
//读取目录的时候它将该目录内容的struct lbuf结构置于该堆栈之上,由slastp开始直到lastp。
if (fflg==0)//如果-f选项无效,则对子目录项进行排序。
qsort(slastp,lastp - slastp,sizeof *lastp,compar);
if (lflg || sflg)//输出目录中文件占用的块的总数用于-l或-s选项
printf("total %D\n", tblocks);//%D相当于现在的%ld
for (ep1=slastp; ep1<lastp; ep1++)//输出子目录每个文件的信息。
pentry(*ep1); //只深入到目录树的下一层,它没有用递归选项
} else
pentry(ep);
}
exit(0);
} pentry(ap) //输出关于文件的信息函数
struct lbuf *ap;
{
struct { char dminor, dmajor;}; //这个变量未被使用!
register t;
register struct lbuf *p;
register char *cp; p = ap;
if (p->lnum == -1)//如果为真则 struct lbuf是无效的。返回。
return;
if (iflg)
printf("%5u ", p->lnum); //索引节点号
if (sflg)
printf("%4D ", nblock(p->lsize));//以块为单位的文件大小
if (lflg) { //以长格式输出
putchar(p->ltype);//输出文件类型
pmode(p->lflags);//输出权限
printf("%2d ", p->lnl);
t = p->luid;
if(gflg)
t = p->lgid;
if (getname(t, tbuf)==0)
printf("%-6.6s", tbuf); //输出用户或组
else
printf("%-6d", t);
if (p->ltype=='b' || p->ltype=='c')//设备:主设备号和从设备号
printf("%3d,%3d", major((int)p->lsize), minor((int)p->lsize));
else
printf("%7ld", p->lsize); //以字节为单位的文件大小
cp = ctime(&p->lmtime);
if(p->lmtime < year) //修改时间
printf(" %-7.7s %-4.4s ", cp+4, cp+20); else//如果早于六个月之前则程序
//输出月、日、年否则输出月、日和时间
printf(" %-12.12s ", cp+4);
}
if (p->lflags&ISARG) //文件名。对于命令行参数,是一个以'\0'结尾的字符串可以
//使用%s
printf("%s\n", p->ln.namep);
else//对于从目录中读取的文件,可能并非以空字符结尾,因此必须使用确切精度的格式字符串%.14s
printf("%.14s\n", p->ln.lname);
} getname(uid, buf)//将用户ID或组ID转换为相应的用户名和组名。
int uid;
char buf[];
{
int j, c, n, i; if (uid==lastuid)//如果为真表示测试成功,返回
return(0);
if(pwdf == NULL) //安全检查
return(-1);
rewind(pwdf); //由文件内容的起始位置开始
lastuid = -1;
do {
i = 0; //buf数组中的索引
j = 0; //行中的计数器 ,记录当前遇到的冒号数:如果是名称则为0,如果是ID则为2。
n = 0; //转换数字值
while((c=fgetc(pwdf)) != '\n') {//读取各行
if (c==EOF)
return(-1);
if (c==':') { //对成员记数
j++;
c = '0';
}
if (j==0) //第一个成员为名称
buf[i++] = c;
if (j==2) //第三个成员为数字ID
n = n*10 + c - '0';
}
} while (n != uid); //一直进行搜索直到找到ID
buf[i++] = '\0';
lastuid = uid;
return(0);
} long
nblock(size) //返回文件使用了多少磁盘块。V7块大小为512字节——物理磁盘分区大小
long size;
{
return((size+511)>>9);//右移9位相当于除以512,从而获得块数。即使文件只有一个字节
//也要占用一个磁盘块。但是1/512的结果为0。所以表达式为'size+511'保证了得到正确的块数
}
//对要检查的权限位以及相应的输出字符进行编码。文件模式中的每个输出字符都有一个数组。
int m1[] = { 1, S_IREAD>>0, 'r', '-' };
int m2[] = { 1, S_IWRITE>>0, 'w', '-' };
int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
int m4[] = { 1, S_IREAD>>3, 'r', '-' };
int m5[] = { 1, S_IWRITE>>3, 'w', '-' };
int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
int m7[] = { 1, S_IREAD>>6, 'r', '-' };
int m8[] = { 1, S_IWRITE>>6, 'w', '-' };
int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; pmode(aflag)
{
register int **mp; flags = aflag;//得到文件权限位(mode_t类型)
for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];)
select(*mp++);//这里以及下面都使用了'++',目的是为了当(权限,字符)对的第一个
//元素在flags中未找到的时候跳过数组中的(权限,字符)对。
//这段代码设计的很精巧、幽雅值得仔细研究
} select(pairp)//从权限,字符数组中选择flags中相应的元素
register int *pairp;
{
register int n; n = *pairp++;
while (--n>=0 && (flags&*pairp++)==0)
pairp++;
putchar(*pairp);
} char *
makename(dir, file) //连接由斜杠分割的目录名和文件名并产生一个字符串
char *dir, *file;
{
static char dfile[100];
register char *dp, *fp;
register int i; dp = dfile;
fp = dir;
while (*fp)
*dp++ = *fp++;
*dp++ = '/';
fp = file;
for (i=0; i<DIRSIZ; i++)
*dp++ = *fp++;
*dp = 0;
return(dfile);
} readdir(dir) //读取命令行指定的目录内容
char *dir;
{
static struct direct dentry;
register int j;
register struct lbuf *ep; if ((dirf = fopen(dir, "r")) == NULL) {//打开用于读取的目录
printf("%s unreadable\n", dir);
return;
}
tblocks = 0;//将全局变量清零。
for(;;) {
if (fread((char *)&dentry, sizeof(dentry), 1, dirf) != 1)
//读取目录项并将其添加到flist数组中
break;
if (dentry.d_ino==0
|| aflg==0 && dentry.d_name[0]=='.' && (dentry.d_name[1]=='\0'
|| dentry.d_name[1]=='.' && dentry.d_name[2]=='\0'))
continue;//跳过不感兴趣的目录项
ep = gstat(makename(dir, dentry.d_name), 0);//第二个参数为0,表明参数不是
//来自命令行,它更新lastp和flist
if (ep==NULL)
continue;
if (ep->lnum != -1)
ep->lnum = dentry.d_ino;//保存索引节点号
for (j=0; j<DIRSIZ; j++)
ep->ln.lname[j] = dentry.d_name[j];//保存文件名
}
fclose(dirf);
} struct lbuf *
gstat(file, argfl)//获得和存储文件信息的核心函数
char *file;
{
extern char *malloc();
struct stat statb;
register struct lbuf *rep;
static int nomocore; if (nomocore) //已经耗尽内存
return(NULL);
rep = (struct lbuf *)malloc(sizeof(struct lbuf));
if (rep==NULL) {
fprintf(stderr, "ls: out of memory\n");
nomocore = 1;
return(NULL);
}
if (lastp >= &flist[NFILES]) {//检查是否给定文件太多
static int msg;
lastp--;
if (msg==0) {
fprintf(stderr, "ls: too many files\n");
msg++;
}
}
*lastp++ = rep; //填充文件信息
rep->lflags = 0;
rep->lnum = 0;
rep->ltype = '-'; //缺省文件类型
if (argfl || statreq) {
if (stat(file, &statb)<0) {
printf("%s not found\n", file);
statb.st_ino = -1;
statb.st_size = 0;
statb.st_mode = 0;
if (argfl) {
lastp--;
return(0);
}
}
rep->lnum = statb.st_ino; //stat()调用成功,拷贝信息
rep->lsize = statb.st_size;
switch(statb.st_mode&S_IFMT) { case S_IFDIR:
rep->ltype = 'd';
break; case S_IFBLK:
rep->ltype = 'b';
rep->lsize = statb.st_rdev;
break; case S_IFCHR:
rep->ltype = 'c';
rep->lsize = statb.st_rdev;
break;
}
rep->lflags = statb.st_mode & ~S_IFMT;
rep->luid = statb.st_uid;
rep->lgid = statb.st_gid;
rep->lnl = statb.st_nlink;
if(uflg)
rep->lmtime = statb.st_atime;
else if (cflg)
rep->lmtime = statb.st_ctime;
else
rep->lmtime = statb.st_mtime;
tblocks += nblock(statb.st_size);
}
return(rep);
} compar(pp1, pp2)//比较函数,用于排序函数qsort()的调用
struct lbuf **pp1, **pp2;
{
register struct lbuf *p1, *p2; p1 = *pp1;
p2 = *pp2;
//下面的代码中返回负数表示第一个文件相对于第二个文件应该位于数组的前面,0表示
//两个文件相等,1表示第二个文件相对于第一个文件应该位于数组的前面
if (dflg==0) {//如果必须读取目录
if (p1->lflags&ISARG && p1->ltype=='d') {//p1是一个命令行参数且p1是一个目录
if (!(p2->lflags&ISARG && p2->ltype=='d'))//p2不是一个命令行参数且p2不是一个目录
return(1);//第一种情况在第二种情况之后
} else {//p1不是一个命令行目录
if (p2->lflags&ISARG && p2->ltype=='d')//p2是一个命令行参数且p2是一个目录
return(-1);//第一种情况在第二种情况之前
}
}
if (tflg) {//排序是基于时间的
if(p2->lmtime == p1->lmtime)//p2的时间等于p1的时间
return(0);
if(p2->lmtime > p1->lmtime)//p2的时间大于p1的时间
return(rflg);//返回rflg的值(正或负)
return(-rflg);//p2的时间小于p1的时间
}
//返回相乘的结果
return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname,
p2->lflags&ISARG? p2->ln.namep: p2->ln.lname));
} /****************************************
移植性问题。
若将V7 的ls 命令移植到现代操作系统中会遇到些问题,使其无法编译通过。
①。V7 的ls 中定义的函数如 readdir()、select()等函数会与系统库函数发生冲突,即名字
污染。移植的时候,要把这些函数改名方可。
②。V7 的ls 中读目录所使用的函数readdir()使用fopen()、fread()函数进行读目录,这与
现代操作系统中读取目录信息发生冲突,使读目录无法正常工作。(现代Linux中使用
opendir()、readdir()(与本程序的版本不同,它是一个系统调用))
③。V7 的ls中目录名的长度限定在14个字符。即#define DIRSIZ 14 但现代操作系统中没有
这个定义。移植后,编译出错。
④。而且这个DIRSIZ 对于现代所允许的目录名长度显得太小。
⑤......
......
等等
本文把V7 的ls 移植到REDHAT 9中出现了上述问题。
移植后的相关修改。
1。把readdir()、select()函数改成其他和系统调用不冲突的名字。
2。在程序开始处添加 #define DIRSIZ 14
3。在原readdir()函数内部添加或修改
static struct direct *dentry;
.....
DIR *dp;
将 if ((dirf = fopen(dir, "r")) == NULL) {//打开用于读取的目录
printf("%s unreadable\n", dir);
return;
}
改成 if(( dp = opendir(dir)) == NULL) {
printf("%s unreadable\n", dir);
return;
}
将 if (fread((char *)&dentry, sizeof(dentry), 1, dirf) != 1)
改成 if( dentry = readdir(dp) == NULL )
把所有的dentry. 改成 dentry->
这样初步修改后,程序基本可以正常运行,只是对于目录名超过14个字符的,无法正常读取
等等。
****************************************/
* 对UNUX V7 ls命令原代码的注释。
* 位置:UNIX V7/usr/src/cmd/ls.c
*/ #include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <stdio.h> #define NFILES 1024
FILE *pwdf, *dirf;
char stdbuf[BUFSIZ];
/* struc lbuf 封装了对ls有用的struct stat */
struct lbuf {
union {
char lname[15];
char *namep; //文件名
} ln;
char ltype; //文件类型
short lnum; //索引结点号
short lflags; //权限位
short lnl; //链接数
short luid; //用户ID
short lgid; //组ID
long lsize; //文件大小
long lmtime; //访问时间
};
/* 每个选项设置一个标记变量 */
int aflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg;
int rflg = 1; //颠倒排序标志 如果使用-r 选项,则rflg会被置-1
long year; //这个变量被设置为从1970年1月1日午夜到当前时间六个月前所经过的秒数
int flags;
int lastuid = -1; //用户ID用于测试
char tbuf[16];
long tblocks; //文件所占用的磁盘块数
int statreq; //使用stat()系统调用的标志,如果为 1 则表示使用stat获取目录信息
struct lbuf *flist[NFILES];
struct lbuf **lastp = flist;
struct lbuf **firstp = flist;//以上三个变量表示用ls 显示信息的文件
char *dotp = "."; //设置默认读取的目录,即当前目录 char *makename(); //连接由斜杠字符分割的目录名和文件名并产生一个字符串
struct lbuf *gstat(); //读取和存储文件信息的核心函数
char *ctime(); //获得时间的函数
long nblock(); //计算文件使用的磁盘块数 #define ISARG 0100000 //用于区分一个文件是由命令行给定还是由目录中读取 main(argc, argv)
char *argv[];
{
int i;
register struct lbuf *ep, **ep1;
register struct lbuf **slastp;
struct lbuf **epp;
struct lbuf lb;
char *t;
int compar(); setbuf(stdout, stdbuf);
time(&lb.lmtime); //获取当前时间
year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 个月之前 */
if (--argc > 0 && *argv[1] == '-') {
argv++; //跳过程序文件名
while (*++*argv) switch (**argv) {//扫描命令行上第一个参数 case 'a': //输出所有目录项。如果没有该选项,则不输出 '.' 和 '..'。
aflg++;
continue; case 's': //输出文件大小。文件名以字母顺序排列
sflg++;
statreq++;
continue; case 'd': //用于目录参数,输出目录本身的信息,而不是目录的内容
dflg++;
continue; case 'g': //用于 'ls -l',但输出组名而不是用户名
gflg++;
continue; case 'l': //在第一列与文件名或长列表一起输出结点索引号
lflg++;
statreq++;
continue; case 'r': //颠倒顺序排列。文件名以字母顺序或时间顺序进行排列
rflg = -1;
continue; case 't': //以修改时间而不是文件名排列输出结果,最近修改的文件处于最前面
tflg++;
statreq++;
continue; case 'u': //以访问时间而不是修改时间排列输出结果,与-t或 -l一起使用
uflg++;
continue; case 'c': //使用索引结点的改变时间,而不是文件的修改时间。该选项与-t或-l一起使用。
cflg++;
continue; case 'i': //在第一列与文件名或长列表一起输出节点索引号
iflg++;
continue; case 'f': //强迫每个参数都当作目录读取,然后输出在每个目录槽中找到的
//文件名。该选项使-l、-r、-s和-t选项无效,但激活了-a选项
fflg++;
continue; default: //忽略未知的选项字母
continue;
}
argc--; //扫描完第一个参数后,由于argv的移动,将argc减1
}
if (fflg) {
aflg++;
lflg = 0;//-f覆盖了-l、-s和-t选项
sflg = 0;
tflg = 0;
statreq = 0;
}
if(lflg) { //打开口令或组文件
t = "/etc/passwd";
if(gflg) //V7 ls命令仅显示用户和组信息中的一项
t = "/etc/group";
pwdf = fopen(t, "r");
}
if (argc==0) {//如果没有指定参数,则使用当前目录
argc++;
argv = &dotp - 1; //减1 是为了对下面的第139行'++argv'进行弥补
}
for (i=0; i < argc; i++) { //获得每个文件信息
if ((ep = gstat(*++argv, 1))==NULL)//gstat的第二个参数表示如果为1则文件
//由命令行参数给定,否则为假
continue;
ep->ln.namep = *argv; //获得文件名
ep->lflags |= ISARG; //加入ISARG位,用于后面判断文件是由命令行给定还是由
//目录读取
}
qsort(firstp, lastp - firstp, sizeof *lastp, compar);//对文件进行排序,这是个
//库中的排序函数
slastp = lastp; //将lastt当前值保存在slastp中
for (epp=firstp; epp<slastp; epp++) {//对数组的每个元素进行循环以适当的方式
//输出文件或目录信息
ep = *epp;
if (ep->ltype=='d' && dflg==0 || fflg) {//如果文件类型为目录并且未提供-d
//选项或提供了-f选项,则ls必须读取该目录而不是输出该目录本身的信息
if (argc>1)//如果命令行提供了多个文件则输出目录名和一个冒号
printf("\n%s:\n", ep->ln.namep);
lastp = slastp; //重设lastp
readdir(ep->ln.namep);
//flist数组作为一个两层的文件名堆栈。命令行参数通过slastp-1保存在flist中,当readdir
//读取目录的时候它将该目录内容的struct lbuf结构置于该堆栈之上,由slastp开始直到lastp。
if (fflg==0)//如果-f选项无效,则对子目录项进行排序。
qsort(slastp,lastp - slastp,sizeof *lastp,compar);
if (lflg || sflg)//输出目录中文件占用的块的总数用于-l或-s选项
printf("total %D\n", tblocks);//%D相当于现在的%ld
for (ep1=slastp; ep1<lastp; ep1++)//输出子目录每个文件的信息。
pentry(*ep1); //只深入到目录树的下一层,它没有用递归选项
} else
pentry(ep);
}
exit(0);
} pentry(ap) //输出关于文件的信息函数
struct lbuf *ap;
{
struct { char dminor, dmajor;}; //这个变量未被使用!
register t;
register struct lbuf *p;
register char *cp; p = ap;
if (p->lnum == -1)//如果为真则 struct lbuf是无效的。返回。
return;
if (iflg)
printf("%5u ", p->lnum); //索引节点号
if (sflg)
printf("%4D ", nblock(p->lsize));//以块为单位的文件大小
if (lflg) { //以长格式输出
putchar(p->ltype);//输出文件类型
pmode(p->lflags);//输出权限
printf("%2d ", p->lnl);
t = p->luid;
if(gflg)
t = p->lgid;
if (getname(t, tbuf)==0)
printf("%-6.6s", tbuf); //输出用户或组
else
printf("%-6d", t);
if (p->ltype=='b' || p->ltype=='c')//设备:主设备号和从设备号
printf("%3d,%3d", major((int)p->lsize), minor((int)p->lsize));
else
printf("%7ld", p->lsize); //以字节为单位的文件大小
cp = ctime(&p->lmtime);
if(p->lmtime < year) //修改时间
printf(" %-7.7s %-4.4s ", cp+4, cp+20); else//如果早于六个月之前则程序
//输出月、日、年否则输出月、日和时间
printf(" %-12.12s ", cp+4);
}
if (p->lflags&ISARG) //文件名。对于命令行参数,是一个以'\0'结尾的字符串可以
//使用%s
printf("%s\n", p->ln.namep);
else//对于从目录中读取的文件,可能并非以空字符结尾,因此必须使用确切精度的格式字符串%.14s
printf("%.14s\n", p->ln.lname);
} getname(uid, buf)//将用户ID或组ID转换为相应的用户名和组名。
int uid;
char buf[];
{
int j, c, n, i; if (uid==lastuid)//如果为真表示测试成功,返回
return(0);
if(pwdf == NULL) //安全检查
return(-1);
rewind(pwdf); //由文件内容的起始位置开始
lastuid = -1;
do {
i = 0; //buf数组中的索引
j = 0; //行中的计数器 ,记录当前遇到的冒号数:如果是名称则为0,如果是ID则为2。
n = 0; //转换数字值
while((c=fgetc(pwdf)) != '\n') {//读取各行
if (c==EOF)
return(-1);
if (c==':') { //对成员记数
j++;
c = '0';
}
if (j==0) //第一个成员为名称
buf[i++] = c;
if (j==2) //第三个成员为数字ID
n = n*10 + c - '0';
}
} while (n != uid); //一直进行搜索直到找到ID
buf[i++] = '\0';
lastuid = uid;
return(0);
} long
nblock(size) //返回文件使用了多少磁盘块。V7块大小为512字节——物理磁盘分区大小
long size;
{
return((size+511)>>9);//右移9位相当于除以512,从而获得块数。即使文件只有一个字节
//也要占用一个磁盘块。但是1/512的结果为0。所以表达式为'size+511'保证了得到正确的块数
}
//对要检查的权限位以及相应的输出字符进行编码。文件模式中的每个输出字符都有一个数组。
int m1[] = { 1, S_IREAD>>0, 'r', '-' };
int m2[] = { 1, S_IWRITE>>0, 'w', '-' };
int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
int m4[] = { 1, S_IREAD>>3, 'r', '-' };
int m5[] = { 1, S_IWRITE>>3, 'w', '-' };
int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
int m7[] = { 1, S_IREAD>>6, 'r', '-' };
int m8[] = { 1, S_IWRITE>>6, 'w', '-' };
int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; pmode(aflag)
{
register int **mp; flags = aflag;//得到文件权限位(mode_t类型)
for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];)
select(*mp++);//这里以及下面都使用了'++',目的是为了当(权限,字符)对的第一个
//元素在flags中未找到的时候跳过数组中的(权限,字符)对。
//这段代码设计的很精巧、幽雅值得仔细研究
} select(pairp)//从权限,字符数组中选择flags中相应的元素
register int *pairp;
{
register int n; n = *pairp++;
while (--n>=0 && (flags&*pairp++)==0)
pairp++;
putchar(*pairp);
} char *
makename(dir, file) //连接由斜杠分割的目录名和文件名并产生一个字符串
char *dir, *file;
{
static char dfile[100];
register char *dp, *fp;
register int i; dp = dfile;
fp = dir;
while (*fp)
*dp++ = *fp++;
*dp++ = '/';
fp = file;
for (i=0; i<DIRSIZ; i++)
*dp++ = *fp++;
*dp = 0;
return(dfile);
} readdir(dir) //读取命令行指定的目录内容
char *dir;
{
static struct direct dentry;
register int j;
register struct lbuf *ep; if ((dirf = fopen(dir, "r")) == NULL) {//打开用于读取的目录
printf("%s unreadable\n", dir);
return;
}
tblocks = 0;//将全局变量清零。
for(;;) {
if (fread((char *)&dentry, sizeof(dentry), 1, dirf) != 1)
//读取目录项并将其添加到flist数组中
break;
if (dentry.d_ino==0
|| aflg==0 && dentry.d_name[0]=='.' && (dentry.d_name[1]=='\0'
|| dentry.d_name[1]=='.' && dentry.d_name[2]=='\0'))
continue;//跳过不感兴趣的目录项
ep = gstat(makename(dir, dentry.d_name), 0);//第二个参数为0,表明参数不是
//来自命令行,它更新lastp和flist
if (ep==NULL)
continue;
if (ep->lnum != -1)
ep->lnum = dentry.d_ino;//保存索引节点号
for (j=0; j<DIRSIZ; j++)
ep->ln.lname[j] = dentry.d_name[j];//保存文件名
}
fclose(dirf);
} struct lbuf *
gstat(file, argfl)//获得和存储文件信息的核心函数
char *file;
{
extern char *malloc();
struct stat statb;
register struct lbuf *rep;
static int nomocore; if (nomocore) //已经耗尽内存
return(NULL);
rep = (struct lbuf *)malloc(sizeof(struct lbuf));
if (rep==NULL) {
fprintf(stderr, "ls: out of memory\n");
nomocore = 1;
return(NULL);
}
if (lastp >= &flist[NFILES]) {//检查是否给定文件太多
static int msg;
lastp--;
if (msg==0) {
fprintf(stderr, "ls: too many files\n");
msg++;
}
}
*lastp++ = rep; //填充文件信息
rep->lflags = 0;
rep->lnum = 0;
rep->ltype = '-'; //缺省文件类型
if (argfl || statreq) {
if (stat(file, &statb)<0) {
printf("%s not found\n", file);
statb.st_ino = -1;
statb.st_size = 0;
statb.st_mode = 0;
if (argfl) {
lastp--;
return(0);
}
}
rep->lnum = statb.st_ino; //stat()调用成功,拷贝信息
rep->lsize = statb.st_size;
switch(statb.st_mode&S_IFMT) { case S_IFDIR:
rep->ltype = 'd';
break; case S_IFBLK:
rep->ltype = 'b';
rep->lsize = statb.st_rdev;
break; case S_IFCHR:
rep->ltype = 'c';
rep->lsize = statb.st_rdev;
break;
}
rep->lflags = statb.st_mode & ~S_IFMT;
rep->luid = statb.st_uid;
rep->lgid = statb.st_gid;
rep->lnl = statb.st_nlink;
if(uflg)
rep->lmtime = statb.st_atime;
else if (cflg)
rep->lmtime = statb.st_ctime;
else
rep->lmtime = statb.st_mtime;
tblocks += nblock(statb.st_size);
}
return(rep);
} compar(pp1, pp2)//比较函数,用于排序函数qsort()的调用
struct lbuf **pp1, **pp2;
{
register struct lbuf *p1, *p2; p1 = *pp1;
p2 = *pp2;
//下面的代码中返回负数表示第一个文件相对于第二个文件应该位于数组的前面,0表示
//两个文件相等,1表示第二个文件相对于第一个文件应该位于数组的前面
if (dflg==0) {//如果必须读取目录
if (p1->lflags&ISARG && p1->ltype=='d') {//p1是一个命令行参数且p1是一个目录
if (!(p2->lflags&ISARG && p2->ltype=='d'))//p2不是一个命令行参数且p2不是一个目录
return(1);//第一种情况在第二种情况之后
} else {//p1不是一个命令行目录
if (p2->lflags&ISARG && p2->ltype=='d')//p2是一个命令行参数且p2是一个目录
return(-1);//第一种情况在第二种情况之前
}
}
if (tflg) {//排序是基于时间的
if(p2->lmtime == p1->lmtime)//p2的时间等于p1的时间
return(0);
if(p2->lmtime > p1->lmtime)//p2的时间大于p1的时间
return(rflg);//返回rflg的值(正或负)
return(-rflg);//p2的时间小于p1的时间
}
//返回相乘的结果
return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname,
p2->lflags&ISARG? p2->ln.namep: p2->ln.lname));
} /****************************************
移植性问题。
若将V7 的ls 命令移植到现代操作系统中会遇到些问题,使其无法编译通过。
①。V7 的ls 中定义的函数如 readdir()、select()等函数会与系统库函数发生冲突,即名字
污染。移植的时候,要把这些函数改名方可。
②。V7 的ls 中读目录所使用的函数readdir()使用fopen()、fread()函数进行读目录,这与
现代操作系统中读取目录信息发生冲突,使读目录无法正常工作。(现代Linux中使用
opendir()、readdir()(与本程序的版本不同,它是一个系统调用))
③。V7 的ls中目录名的长度限定在14个字符。即#define DIRSIZ 14 但现代操作系统中没有
这个定义。移植后,编译出错。
④。而且这个DIRSIZ 对于现代所允许的目录名长度显得太小。
⑤......
......
等等
本文把V7 的ls 移植到REDHAT 9中出现了上述问题。
移植后的相关修改。
1。把readdir()、select()函数改成其他和系统调用不冲突的名字。
2。在程序开始处添加 #define DIRSIZ 14
3。在原readdir()函数内部添加或修改
static struct direct *dentry;
.....
DIR *dp;
将 if ((dirf = fopen(dir, "r")) == NULL) {//打开用于读取的目录
printf("%s unreadable\n", dir);
return;
}
改成 if(( dp = opendir(dir)) == NULL) {
printf("%s unreadable\n", dir);
return;
}
将 if (fread((char *)&dentry, sizeof(dentry), 1, dirf) != 1)
改成 if( dentry = readdir(dp) == NULL )
把所有的dentry. 改成 dentry->
这样初步修改后,程序基本可以正常运行,只是对于目录名超过14个字符的,无法正常读取
等等。
****************************************/
相关阅读 更多 +