/* GNU's users.
*/
/* Written by jla; revised by djm */
//包含进来3个标准的头文件,其中,getopt.h是用来解析命令行选项的头文件
#include
#include
#include
//继续包含
#include
#include "system.h"
//下面这4个头文件是本软件包的lib文件夹下的头文件
#include "error.h"
#include "long-options.h"
#include "quote.h"
#include "readutmp.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "users" //定义宏PROGRAM_NAME,其值是一个字符串,意义是此程序的名称
#define AUTHORS "Joseph Arceneaux", "David MacKenzie" //定义宏AUTHORS,其值是两个字符串,两个作者的名字
/* The name this program was run with. */
char *program_name;//声明全局字符指针变量program_name
static int
userid_compare (const void *v_a, const void *v_b) //static的意思是此函数只能在本文件里使用,接受两个指针变量的形参
{
char **a = (char **) v_a;//这种赋值方式 好好看看,a是一个指向字符类型变量的指针的指针,在声明时进行了初始化,初始化的值是对形参v_a进行了强制类型转换,转换成的类型是:指向字符类型变量的指针的指针,这个类型正好也和a的类型一致,为什么要定义成这种类型呢澹? char **b = (char **) v_b;
return strcmp (*a, *b);//函数的返回值是通过库函数strcmp的返回值得到的
}
static void
list_entries_users (size_t n, const STRUCT_UTMP *this)//字面上看,这个函数的作用是,列出用户列表来,接受的形参有两个,一个是我熟悉的size_t类型的,另一个是STRUCT_UTMP类型的
{//首先来看看第二个形参的定义,是在lib/readutmp.h里面定义的: typedef struct UTMP_STRUCT_NAME STRUCT_UTMP; 继续追踪# define UTMP_STRUCT_NAME utmpx,这个utmpx类型是在库头# include 里面定义的,具体我进去看了看,这是个颇大的结构体啊,里面的东东还挺多
char **u = xnmalloc (n, sizeof *u);//xnmalloc这个函数是在lib文件下的xalloc.h文件中声明并定义的,这里的语句是分配n个内存空间
size_t i; //声明size_t类型的变量i
size_t n_entries = 0;//初始化size_t类型的变量n_entries,初始值为0
while (n--)//循环的条件是n不等于0,检查完n的值以后,n减一
{
if (IS_USER_PROCESS (this)) //IS_USER_PROCESS是在readutmp.h中定义的,但是this这个变量是形参传入进来的
{//如果if里的条件是真的话,执行一下这个语句块,然后继续执行
char *trimmed_name;//声明字符指针变量
trimmed_name = extract_trimmed_name (this);
/*
char *
extract_trimmed_name (const STRUCT_UTMP *ut)这个是该函数的原型,函数返回的是一个字符指针,和调用的地方也是相符的
*/
u[n_entries] = trimmed_name;//u这个数组是函数刚开始得到的那个内存地址
++n_entries;//每次设置完一个数组元素后,增加数组下标,这样看来,u应该是一个保存了一系列字符指针的数组,也就是指针数组,呵呵
}
this++;//每次执行一次循环,this这个变量增加一次
}
qsort (u, n_entries, sizeof (u[0]), userid_compare);//调用库函数qsort进行排序,这个qsort在 c和指针上面介绍过,需要一个自己写的排序方法,这里是userid_compare
for (i = 0; i < n_entries; i++)//循环设定的次数是n_entries次
{
char c = (i < n_entries - 1 ? ' ' : '\n');//声明字符变量c,次的值是从一个条件语句得到的,i如果小于n_entries-1,则出的值是个空格,否则就是一个回车符
fputs (u[i], stdout);//向标准输出打印u[i]
putchar (c);//打印字符c
}
for (i = 0; i < n_entries; i++)
free (u[i]);//循环释放u这个在函数开始的时候申请的内存空间
free (u);//再次释放最后的一个u
}
/* Display a list of users on the system, according to utmp file FILENAME.
Use read_utmp OPTIONS to read FILENAME. */
static void
users (const char *filename, int options)//users函数,接受两个形参,一个是字符型指针,一个是整型变量
{
size_t n_users;//声明size_t类型的n_users变量
STRUCT_UTMP *utmp_buf;//声明指向STRUCT_UTMP结构体类型的指针变量
if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
/*
read_utmp函数是在lib/readutmp.c里面定义的函数,调用它的结果如果返回的不是0,则调用error函数,否则不执行,这是一种典型的出错控制
*/
error (EXIT_FAILURE, errno, "%s", filename);
list_entries_users (n_users, utmp_buf);//调用完read_utmp函数后,调用本程序前面实现的这个list_entries_users函数
free (utmp_buf);//释放utmp_buf指向的内存空间
}
void
usage (int status)//帮助函数,日后再遇到帮助函数,就不再写注释了,因为这个软件包里的usage函数都长一个样子,看看就行了
{
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else
{
printf (_("Usage: %s [OPTION]... [ FILE ]\n"), program_name);
printf (_("\
Output who is currently logged in according to FILE.\n\
If FILE is not specified, use %s. %s as FILE is common.\n\
\n\
"),
UTMP_FILE, WTMP_FILE);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
exit (status);
}
int
main (int argc, char **argv)//main函数体,比较简练,只有28行
{//下面这5行还是标准做法了
initialize_main (&argc, &argv);
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);//登记出口函数,这里有必要提一下的是,close_stdout这个函数,是在lib/Closeout.c里面实现的,之前创建coreutil项目时,没有把lib文件夹包含进来,导致看一些函数的定义的时候很麻烦,现在都包含进来了,看起来方便多了
parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
usage, AUTHORS, (char const *) NULL);//调用parse_long_options解析命令行参数,主要是打印帮助信息了
if (getopt_long (argc, argv, "", NULL, NULL) != -1)//调用库函数getopt_long,解析命令行选项,如果有选项,则打印帮助并退出程序,注意这个getopt_long库函数调用只在if语句里执行一次,而不是像之前我看的那些程序一样是在while循环里循环执行,这说明这个程序不需要任何的选项
usage (EXIT_FAILURE);
switch (argc - optind)//这个switch开关语句的判断条件是命令行参数的个数减去第一个命令行参数的位置的值,让我们来看看场景,如果程序没有接任何参数,则argc是1,optind的值也是1,相减后的值是0,如果argc是2,说明有命令行参数,而这时候optind的值还是1,这样相减后就是1了
{
case 0: /* users */ //没有命令行参数的情况下,调用的是下面的users函数
users (UTMP_FILE, READ_UTMP_CHECK_PIDS);
break;
case 1: /* users */ //有命令行参数的情况下,传递给users函数的形参是命令行参数和0
users (argv[optind], 0);
break;
default: /* lose *///如果有其他的值,则打印帮助并退出程序
error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
usage (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);//上面没有退出程序的情况下,说明程序已经顺利执行了,这时候退出状态是成功。
}
这个程序使用了读取UTMP的方法,这个读取是我在06年在你好的时候,很羡慕的。
阅读(992) | 评论(0) | 转发(0) |