Chinaunix首页 | 论坛 | 博客
  • 博客访问: 931082
  • 博文数量: 177
  • 博客积分: 8613
  • 博客等级: 中将
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-12 04:16
文章分类
文章存档

2012年(12)

2011年(24)

2010年(24)

2009年(75)

2008年(42)

我的朋友

分类: C/C++

2009-12-12 22:51:45

/* Base64 encode/decode strings or files.
   Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.

   This file is part of Base64.

   Base64 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.

   Base64 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 Base64; see the file COPYING.  If not, write to the Free
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/* Written by Simon Josefsson .  */

#include

#include
#include
#include

#include "system.h"
#include "error.h"
#include "xstrtol.h"
#include "quote.h"
#include "quotearg.h"

#include "base64.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "base64" //定义宏

#define AUTHOR "Simon Josefsson" //定义宏

/* The invocation name of this program.  */
char *program_name;//声明全局字符指针

static const struct option long_options[] = {
  {"decode", no_argument, 0, 'd'},
  {"wrap", required_argument, 0, 'w'},
  {"ignore-garbage", no_argument, 0, 'i'},
  {"help", no_argument, 0, GETOPT_HELP_CHAR},
  {"version", no_argument, 0, GETOPT_VERSION_CHAR},

  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};//初始化一个option类型的结构体数组

static void
usage (int status)//帮助函数
{
  if (status != EXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION] [FILE]\n\
Base64 encode or decode FILE, or standard input, to standard output.\n\
\n"), program_name);
      fputs (_("\
  -w, --wrap=COLS       Wrap encoded lines after COLS character (default 76).\n\
                        Use 0 to disable line wrapping.\n\ 定义一行显示多少个字符
\n\
  -d, --decode          Decode data.\n\ 加密
  -i, --ignore-garbage  When decoding, ignore non-alphabet characters.\n\ 忽略非字母字符
\n\
"), stdout);
      fputs (_("\
      --help            Display this help and exit.\n\
      --version         Output version information and exit.\n"), stdout);
      fputs (_("\
\n\
With no FILE, or when FILE is -, read standard input.\n"), stdout);
      fputs (_("\
\n\
The data are encoded as described for the base64 alphabet in RFC 3548.\n\
When decoding, the input may contain newlines in addition to the bytes of\n\
the formal base64 alphabet.  Use --ignore-garbage to attempt to recover\n\
from any other non-alphabet bytes in the encoded stream.\n"),
         stdout);//这句话的意思说明可以加个长参数,去除待解码的文件里非法的字符
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }

  exit (status);
}

/* Note that increasing this may decrease performance if --ignore-garbage
   is used, because of the memmove operation below.
   这个英文注释的意思是,如果使用--ignore-garbage,会降低性能,因为下面的memmove操作。   */
#define BLOCKSIZE 3072 //定义宏
#define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE) //宏定义

/* Ensure that BLOCKSIZE is a multiple of 3 and 4. 确保BLOCKSIZE是12的最小公倍数 */
#if BLOCKSIZE % 12 != 0//条件编译,如果BLOCKSIZE对12取余,结果不为0,则打印错误
# error "invalid BLOCKSIZE"
#endif

static void
wrap_write (const char *buffer, size_t len,
        uintmax_t wrap_column, size_t *current_column, FILE *out)
{
  size_t written; //声明size_t 类型的局部变量

  if (wrap_column == 0) //如果函数接受的形参wrap_column等于0,则
    {
      /* Simple write. */
      if (fwrite (buffer, 1, len, stdout) < len)//调用库函数fwrite,往stdout写,如果写入的内容小于len,则报错
    error (EXIT_FAILURE, errno, _("write error"));
    }
  else//否则,执行下面的循环体
    for (written = 0; written < len;)//循环的初始条件是written等于0,条件是written小于len,读到这里,应该可以猜测出来,len是整个解码或者编码后目标字符串的总长度,循环的调节是在for循环体里面做的。
      {
      /*
这个for循环执行完毕后,函数就退出了,void类型的函数可以没有return返回值,这也合乎语法的。
看完循环后知道它的作用是,分行输出len个字符,具体的参数需要在调用的地方研究

      */
    uintmax_t cols_remaining = wrap_column - *current_column;//初始化uintmax_t类型的局部变量cols_remaining,它的值是通过两个形参相减得到的。
//初始化size_t类型的变量to_write,他的值是通过MIN这个宏得到的,MIN是在system.h里面定义的,作用是取得两个参数的最小的那个
    size_t to_write = MIN (cols_remaining, SIZE_MAX);
    to_write = MIN (to_write, len - written);//再取一次to_write的值,也是通过MIN这个宏

    if (to_write == 0)//如果变量to_write的值等于0,则向out指向的文件描述符打印一个换行,并将current_column指向的变量置为0
      {
        if (fputs ("\n", out) < 0)
          error (EXIT_FAILURE, errno, _("write error"));
        *current_column = 0;
      }
    else//否则,如果to_write不等于0,则向标准输出打印从buffer + written开始的to_write个字符,并将current_column指向的变量增加to_write,然后written增加to_write
      {
        if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
          error (EXIT_FAILURE, errno, _("write error"));
        *current_column += to_write;
        written += to_write;
      }
      }
}

static void
do_encode (FILE *in, FILE *out, uintmax_t wrap_column)//编码函数
{
  size_t current_column = 0;//初始化size_t类型的变量current_column,初始值为0
  char inbuf[BLOCKSIZE];//声明字符类型的数组
  char outbuf[B64BLOCKSIZE];//声明字符类型的数组
  size_t sum;//声明size_t类型的局部变量

  do//这个do-while循环构成了编码函数的主体
    {
      size_t n;//声明size_t类型的局部变量n,n只在循环内部有效

      sum = 0;//每次循环,sum的值都初始化为0
      do
          /*
这个小的do-while循环的作用是,
          */
    {
      n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);//从in指向的文件读BLOCKSIZE个字符,保存到inbuf数组中
      sum += n;//sum增加n
    }
      while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);//满足这3个条件的情况下,循环体继续执行,看来每次最多就读BLOCKSIZE个字符啊

      if (sum > 0)//如果sum大于0,说明读到东西了
    {
      /* Process input one block at a time.  Note that BLOCKSIZE %
         3 == 0, so that no base64 pads will appear in output. */
      base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));//base64_encode这个函数哪里来的哦?

      wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
              ¤t_column, out);//调用wrap_write,进行输出工作
    }
    }
  while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);//满足这3个条件的情况下,循环体才继续,最后应该是读到in的末尾了,循环就结束了

  /* When wrapping, terminate last line. */
  if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)//这句话的意义主要是为了输出一个换行符
    error (EXIT_FAILURE, errno, _("write error"));

  if (ferror (in))//调用库函数ferror对in文件描述符进行错误检查
    error (EXIT_FAILURE, errno, _("read error"));
}

static void
do_decode (FILE *in, FILE *out, bool ignore_garbage)//解码函数
{
  char inbuf[B64BLOCKSIZE];//声明字符数组
  char outbuf[BLOCKSIZE];//声明字符数组
  size_t sum;//声明局部变量
  struct base64_decode_context ctx;//声明结构体类型ctx

  base64_decode_ctx_init (&ctx);//调用函数base64_decode_ctx_init进行初始化,这个函数也不知道哪里来的,待会分析完以后,我得用strace看看系统调用

  do//这个do循环对应的循环条件是最后的那个打开in文件是否结束,也就是说,这个循环的作用是打开in这个文件,进行操作的
    {
      bool ok;//声明循环内的局部布尔变量ok
      size_t n;//声明局部变量
      unsigned int k;//声明局部变量

      sum = 0;//每次循环开始,sum的值都初始化为0
      do
    {
      n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);//每次读B64BLOCKSIZE个字符都inbuf数组里,最后不够B64BLOCKSIZE个的时候,读到的是剩余的字符

      if (ignore_garbage)//如果函数接受到的形参ignore_garbage是真,则执行下面的语句块,否则不执行
        {
          size_t i;//声明局部变量
          for (i = 0; n > 0 && i < n;)//i循环从0到n-1
        if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')//如果inbuf数组中的当前字符满足isbase64函数的判定,或者是字符'=',则继续进行下一个字符的判断
          i++;
        else//否则,调用memmove,将此非法字符移除,移除的方法是将后面的字符从内存里移动过来,覆盖非法字符,看起来这样做的确比较耗费资源啊
          memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
        }

      sum += n;//sum增加n的值,n的值开始一直是B64BLOCKSIZE的值,最后才是那个余额的值

      if (ferror (in))//调用ferror库函数检查错误
        error (EXIT_FAILURE, errno, _("read error"));
    }
      while (sum < B64BLOCKSIZE && !feof (in));//循环的执行条件是这两个,每次最多只读B64BLOCKSIZE个字符

      /* The following "loop" is usually iterated just once.
     However, when it processes the final input buffer, we want
     to iterate it one additional time, but with an indicator
     telling it to flush what is in CTX.  */
     //下面这个循环的作用是调用base64_decode,对in的内容进行解码
      for (k = 0; k < 1 + feof (in); k++)
    {
      if (k == 1 && ctx.i == 0)
        break;
      n = BLOCKSIZE;
      ok = base64_decode (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);

      if (fwrite (outbuf, 1, n, out) < n)
        error (EXIT_FAILURE, errno, _("write error"));

      if (!ok)
        error (EXIT_FAILURE, 0, _("invalid input"));
    }
    }
  while (!feof (in));
}

int
main (int argc, char **argv)
{
//声明变量
  int opt;
  FILE *input_fh;
  const char *infile;

  /* True if --decode has bene given and we should decode data. */
  bool decode = false;//初始化变量
  /* True if we should ignore non-alphabetic characters. */
  bool ignore_garbage = false;//初始化变量
  /* Wrap encoded base64 data around the 76:th column, by default. */
  uintmax_t wrap_column = 76;//初始化变量

  initialize_main (&argc, &argv);
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);
//下面的这个while循环终于可以看明白了,调用getopt_long,对命令行选项进行解析
  while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1)
    switch (opt)
      {
      case 'd':
    decode = true;
    break;

      case 'w':
    if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
      error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
         quotearg (optarg));
    break;

      case 'i':
    ignore_garbage = true;
    break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR);

      default:
    usage (EXIT_FAILURE);
    break;
      }

  if (argc - optind > 1)//如果argc的值减去optind大于1,则发生错误并打印帮助信息
    {
      error (0, 0, _("extra operand %s"), quote (argv[optind]));
      usage (EXIT_FAILURE);
    }

  if (optind < argc)//如果optind的值小于argc,则infile是第一个命令行参数
    infile = argv[optind];
  else//否则,infile从标准输入读取
    infile = "-";

  if (strcmp (infile, "-") == 0)
    input_fh = stdin;//这里调用strcmp对infile进行判断,如果是'-',则将其值设置为标准输入
  else
    {
      input_fh = fopen (infile, "r");//否则,打开相应的文件
      if (input_fh == NULL)//出错控制,在打开文件描述符的时候,需要检查指针是否为空
    error (EXIT_FAILURE, errno, "%s", infile);
    }

  if (decode)//如果decode的值为真,则调用do_decode函数进行解码
    do_decode (input_fh, stdout, ignore_garbage);
  else//否则调用do_encode函数进行编码
    do_encode (input_fh, stdout, wrap_column);

  if (fclose (input_fh) == EOF)//调用fclose库函数关闭input_fh所指向的文件,fclose正常关闭时返回0,出错的时候返回EOF
    {
      if (strcmp (infile, "-") == 0)//如果infile是标准输入,则打印的错误信息是下面的这样子
    error (EXIT_FAILURE, errno, _("closing standard input"));
      else//否则打印保存在errno中的错误,通过man也可以看到,如果fclose出错的话,的确是会设置errno这个变量的
    error (EXIT_FAILURE, errno, "%s", infile);
    }

  exit (EXIT_SUCCESS);//程序退出状态为成功。
}


看完程序后,用strace跟踪一下base64产生的系统调用:

# strace base64 t
execve("/usr/bin/base64", ["base64", "t"], [/* 20 vars */]) = 0
brk(0)                                  = 0x84f2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b1d8a4a9000
uname({sys="Linux", node="bjurs131.163.com", ...}) = 0
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=65454, ...}) = 0
mmap(NULL, 65454, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2b1d8a4aa000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\331\341p9\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1699880, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b1d8a4ba000
mmap(0x3970e00000, 3481848, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3970e00000
mprotect(0x3970f4a000, 2093056, PROT_NONE) = 0
mmap(0x3971149000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x149000) = 0x3971149000
mmap(0x397114e000, 16632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x397114e000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b1d8a4bb000
arch_prctl(ARCH_SET_FS, 0x2b1d8a4bb250) = 0
mprotect(0x3971149000, 16384, PROT_READ) = 0
mprotect(0x3970c1a000, 4096, PROT_READ) = 0
munmap(0x2b1d8a4aa000, 65454)           = 0
brk(0)                                  = 0x84f2000
brk(0x8513000)                          = 0x8513000
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=56405312, ...}) = 0
mmap(NULL, 56405312, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2b1d8a4bc000
close(3)                                = 0
open("t", O_RDONLY)                     = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b1d8da87000
read(3, "hello , world !\n", 4096)      = 16
read(3, "", 4096)                       = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b1d8da88000
write(1, "aGVsbG8gLCB3b3JsZCAhCg==\n", 25aGVsbG8gLCB3b3JsZCAhCg==
) = 25
close(3)                                = 0
munmap(0x2b1d8da87000, 4096)            = 0
close(1)                                = 0
munmap(0x2b1d8da88000, 4096)            = 0
exit_group(0)                           = ?

    可以看到,里面的确有一些read  write等这样的系统调用,但是对于库函数的调用就看不到啦。

阅读(2199) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~