记录技术 分享生活

一个不知道该写什么的人

记录技术 分享生活

进程终止

  1. 退出函数

    1
    2
    3
    4
    5
    6
    7
    8
    #include <stdlib.h>

    void exit(int status);
    void _Exit(int status);

    #include <unistd.h>

    void _exit(int status);
  2. 函数atexit

    1
    2
    3
    #include <stdlib.h>

    int atexit(void (*func)(void));

    其中,atexit的参数是一个函数地址,当调用此函数时无需向它传递任何参数,也不期望它返回一个值。exit调用这些函数的顺序与它们等级时候的顺序相反。同一函数如若登记多次也会被调用多次。

环境表

环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针的数组的地址:

1
extern char **environ;

我们称environ环境指针(environment pointer),指针数组为环境表,其中各指针指向的字符串为环境字符串。
通常用getenvputenv函数来访问特定的环境变量,而不是environ变量。

C程序的存储空间布局

  • 正文段。
  • 初始化数据段。
  • 未初始化数据段。
  • 栈。
  • 堆。

存储空间分配

  1. malloc,分配指定字节的存储区。此存储区中的初始值不确定。
  2. calloc,为指定数量指定长度的对象分配存储空间。该空间中的每一位(bit)都初始化为0。
  3. realloc,增加或减少以前分配区的长度。
1
2
3
4
5
6
7
8
9
#include <stdlib.h>

void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(size_t nobj, size_t newsize);
/*
* 3个函数返回值:若成功,返回非空指针;若出错,返回NULL
*/
void free(void *ptr);

环境变量

1
2
3
4
#include <stdlib.h>

char **getenv(const char *name);
/* 返回值:指向与name关联的value的指针;若未找到,返回NULL */

getenv,可以用其取环境变量值

1
2
3
4
5
6
7
#include <stdlib.h>

int putenv(char *str);
/* 函数返回值:若成功,返回0;若出错,返回非0 */
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
/* 两个函数返回值:若成功,返回0;若出错,返回-1 */
  • putenv取形式为name=value的字符串,将其放到环境中。
  • setenvname设置为value。如果在环境中name已存在,那么(a)若rewrite非0,则首先删除其现有的定义;(b)若rewrite为0,则不删除其现有定义(name不设置为新value,而且也不出错)。
  • unsetenv删除name的定义。即使不存在这种定义与不出错。

函数setjmplongjmp

1
2
3
4
5
#include <setjmp.h>

int setjmp(jmp_buf env);
/* 返回值:若直接调用,返回0;若从longjmp返回,则为非0 */
void longjmp(jmp_buf env, int val);

在希望返回到的位置调用setjmp。当调用longjmp是,第一个参数是调用setjmp时的env;第二个参数是具有非0值的val,它将称为从setjmp处返回的值。
在课本的例程7.13中,全局变量、静态变量和易失变量(volatile variables)不受优化的影响。如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。但是一个系统移植到另一个系统,其他任何事情都可能改变。

函数getrlimitsetrlimit

1
2
3
4
5
#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);
/* 两个函数返回值:若成功,返回0;若出错,返回非0 */

这两个函数的每一次调用都指定一个资源以及一个指向下列结构的指针。

1
2
3
4
struct rlimit {
rlim_t rlim_cur; /* soft limit: current limit */
rlim_t rlim_max; /* hard limit: maximum value for rlim_cur */
};
  1. 任何一个进程都可以将一个限制值更改为小于或等于其硬限制值。
  2. 任何一个进程都可以降低其硬限制值,但它必须大于或等于其软限制值。
  3. 只有超级用户进程才可以提高硬限制值。

流和FILE对象

对于ASCII字符集,一个字符用于一个字节表示。对于国际字符集,一个字符可用于多个字节表示。标准I/O文件流可用于单字节或多字节(“宽”)字符集。流的定向(stream’s orientation)决定了所读、所写的字符是单字节还是多字节。

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <wchar.h>

int fwide(FILE *fp, int mode);
/*
* 返回值:若流是宽定向的,返回正值;
* 若流是字节定向的,返回负值;若流是未定向的,返回0
* 如果mode参数值为负,fwide将试图使指定的流是字节定向的。
* 如果mode参数值为正,fwide将试图使指定的流是宽定向的。
* 如果mode参数值为0,fwide将不试图设置流的定向,但返回标识该流定向的值。
*/

标准输入、标准输出和标准错误

可以通过预定义文件指针stdinstdoutstderr加以引用。

缓冲

  • 全缓冲。在这种情况下,在填满标准I/O缓冲区才进行实际I/O操作。
    冲洗(flush)说明标准I/O缓冲区的写操作。也可以直接调用fflush函数冲洗一个流。在终端驱动程序中,flush(刷清)表示丢弃已存储在缓冲区中的数据。
  • 行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
  • 不带缓冲。标准I/O库不对字符进行缓冲存储。标准错误流stderr通常是不带缓冲的。
1
2
3
4
5
6
7
#include <stdio.h>

void setbuf(FILE *restrict fp, char *restrict buf);
void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
/*
* 返回值:若成功,返回0;若出错,返回非0
*/

这两个函数可用于更改缓冲类型。

  • _IOFBF 全缓冲
  • _IOLBF 行缓冲
  • _IONBF 不带缓冲
1
2
3
4
#include <stdio.h>

int fflush(FILE *fp);
/* 返回值:若成功,返回0;若出错,返回EOF */

打开流

1
2
3
4
5
6
#include <stdio.h>

FILE *fopen(const char *restrict ptahname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);
/* 3个函数的返回值:若成功,返回文件指针;若出错,返回NULL */
  1. fopen函数打开路径名为pathname的一个指定的文件。
  2. freopen函数在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。
  3. fdopen函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。

调用fclose关闭一个打开的流。

1
2
3
4
#include <stdio.h>

int fclose(FILE *fp);
/* 返回值:若成功,返回0;若返回,返回EOF */

在该文件被关闭之前,冲洗缓冲区中的输出数据。

读和写流

  1. 输入函数
    1
    2
    3
    4
    5
    6
    #include <stdio.h>

    int getc(FILE *fp);
    int fgetc(FILE *fp);
    int getchar(void);
    /* 3个函数的返回值:若成功,返回下一个字符;若已到达文件尾端或出错,返回EOF */
    函数getchar等同于getc(stdin)。前两个函数的区别是,getc可被实现为宏,而fgetc不能实现为宏。
1
2
3
4
5
6
#include <stdio.h>

int ferror(FILE *fp);
int feof(FILE *fp);
/* 两个函数返回值:若条件为真,返回非0(真);否则,返回0(假) */
void clearerr(FILE *fp);

为了区别是到达文件结尾还是出错。
例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
int main(void)
{
FILE*stream;
/*openafileforwriting*/
stream=
fopen("DUMMY.FIL","w");
/*forceanerrorconditionbyattemptingtoread*/
(void)
getc(stream);
if(ferror(stream))/*testforanerroronthestream*/
{
/*displayanerrormessage*/

printf("ErrorreadingfromDUMMY.FIL\n");
/*resettheerrorandEOFindicators*/

clearerr(stream);
}

fclose(stream);
return0;
}

从流中读取数据后,可以调用ungetc将字符再压送回流中。

1
2
3
4
#include <stdio.h>

int ungetc(int c, FILE *fp);
/* 返回值:若成功,返回c;若出错,返回EOF */
  1. 输出函数
    1
    2
    3
    4
    5
    6
    #include <stdio.h>

    int putc(int c, FILE *fp);
    int fputc(int c, FILE *fp);
    int putchar(int c);
    /* 3个函数返回值:若成功,返回c;若出错,返回EOF */

每次一行I/O

下面两个函数提供每次输入一行的功能。

1
2
3
4
5
#include <stdio.h>

char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
/* 两个函数返回值:若成功,返回buf;若已到达文件尾端或出错,返回NULL */

fgets函数一直读到下一个换行符为止,但是不超过n-1个字符,读入的字符被送入缓冲区。

fputsputs提供每次输入一行的功能。

1
2
3
4
5
#include <stdio.h>

int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
/* 两个函数返回值:若成功,返回非负值;若出错,返回EOF */

函数fputs将一个以null字节终止的字符串写到指定的流,尾端的终止符null不写出。

二进制I/O

1
2
3
4
5
#include <stdio.h>

size_t fread(void *restrict ptr, size_t size, size_t nboj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
/* 两个函数的返回值:读或写的对象数 */

指定size为每个数组元素的长度,nobj为欲写元素个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 写数组 */
float data[10];

if (fwrite(&data[2], sizeof(float), 4, fp) != 4)
err_sys("fwrite error");

/* 写一个结构体 */
struct {
short count;
long total;
char name[NAMESIZE];
} item;

if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys("fwrite error");

使用二进制I/O的基本问题,它只能用于读在同一个系统上已写的数据

定位流(Positioning a Stream)

1
2
3
4
5
6
7
8
9
#include <stdio.h>

long ftell(FILE *fp);
/* 返回值:若成功,返回当前文件位置指示;若出错,返回-1L */

int fseek(FILE *fp, long offset, int whence);
/* 返回值:若成功,返回0;若出错,返回-1 */

void rewind(FILE *fp);

whence的值与lseek函数的相同:SEEK_SET表示从文件的起始位置开始,SEEK_CUR表示从当前文件位置开始,SEEK_END表示从文件的尾端开始。offset,文件偏移量。

1
2
3
4
5
6
7
#include <stdio.h>

off_t ftello(FILE *fp);
/* 返回值:若成功,返回当前文件位置;若出错,返回(off_t)-1 */

int fseeko(FILE *fp, off_t offset, int whence);
/* 返回值:若成功,返回0;若出错,返回-1 */
1
2
3
4
5
#include <stdio.h>

int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
/* 两个函数返回值:若成功,返回0;若出错,返回非0 */

格式化I/O

  1. 格式化输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>

    int printf(const char *restrict format, ...);
    int fprintf(FILE *restrict fp, const char *restrict format, ...);
    int dprintf(int fd, const char *restrict format, ...);
    /* 3个函数返回值:若成功,返回输入字符数;若出错,返回负值 */

    int sprintf(char *restrict buf, const char *restrict format, ...);
    /* 返回值:若成功,返回输入数组的字符数;若编码出错,返回负值 */

    int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
    /* 返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值 */
    printf将格式化数据写到标准输出,fprintf写至指定的流,dprintf写至制定的文件描述符,sprintf将格式化的字符送入数组buf。

下面5种printf族的变体,将可变参数表(…)替换成了arg。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdarg.h>
#include <stdio.h>

int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
int vdprintf(int fd, const char *restrict format, va_list arg);
/* 3个函数返回值:若成功,返回输入字符数;若出错,返回负值 */

int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
/* 返回值:若成功,返回输入数组的字符数;若编码出错,返回负值 */

int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
/* 返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值 */
  1. 格式化输入
    1
    2
    3
    4
    5
    6
    #include <stdio.h>

    int scanf(const char *restrict format, ...);
    int fscanf(FILE *restrict fp, const char *restrict format, ...);
    int sscanf(const *restrict buf, const char *restrict format, ...);
    /* 3个函数返回值:赋值的输入项数;若输入出错或在任一转换前已到达文件尾端,返回EOF */

临时文件

1
2
3
4
5
6
7
#include <stdio.h>

char *tmpnam(char *ptr);
/* 返回值:指向唯一路径名的指针 */

FILE *tmpfile(void);
/* 返回值:若成功,返回文件指针;若出错,返回NULL */

tmpnam函数产生一个与现有文件名不同的一个有效路径名字符串。如果ptr是NULL,则产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。后续调用tmpnam会从写该静态区。如过ptr不是NULL,则认为它应该指向长度至少是L_tmpnam个字符的数组。所产生的路径名存放在该数组中,ptr也作为函数值返回。
tmpfile创建一个临时二进制文件(wb+),在关闭该文件或程序结束时将自动删除这种文件。

1
2
3
4
5
6
7
#include <stdlib.h>

char *mkdtemp(char *template);
/* 返回值:若成功,返回指向目录名的指针;若出错,返回NULL */

int mkstemp(char *template);
/* 返回值:若成功,返回文件描述符;若出错,返回-1 */

mkdtemp函数创建了一个目录,该目录有唯一的名字;mkstemp函数创建了一个文件,该文件有一个唯一的名字。名字是通过template字符串进行选择的。这个字符串后6位设置为XXXXXX的路径名。

内存流

1
2
3
4
#include <stdio.h>

FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
/* 返回值:若成功,返回流指针;若出错,返回NULL */

fmemopen函数允许调用者提供缓冲区用于内存流:buf参数指向缓冲区的开始位置,size参数指定了缓冲区大小的字节数。

1
2
3
4
5
6
#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep);

#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
/* 两个函数的返回值:若成功,返回流指针;若出错,返回NULL */

open_memstream函数创建的流是面向字节的,open_wmemstream函数创建的流是面向宽字节的。

  • 创建的流只能打开;
  • 不能指定自己的缓冲区,但可以分别通过bufpsizep参数访问缓冲区地址和大小;
  • 关闭后需要自行释放缓冲区;
  • 对流添加字节会增加缓冲区大小。

为什么选择tree这个命令?

学完APUE的前几章,学完了IO和文件与目录,我们可以做点东西出来了。而借鉴别人的程序就是做出东西的选择之一。

如何下载源代码

http://mama.indstate.edu/users/ice/tree/src/tree-1.7.0.tgz这个链接可以下载到最新的tree源代码,也可以编译成功。

怎么分析源代码

这是个很大的问题,暂时我还没有太多的经验 :(

我们的目的

我们的目的暂时定为搞懂如何遍历所有的目录,并输出。

各部分的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
tree-1.7.0
├── color.c
├── hash.c
├── html.c
├── INSTALL
├── json.c
├── LICENSE
├── Makefile
├── strverscmp.c
├── tree.c
├── tree.h
├── unix.c
└── xml.c

核心的就是这几个文件。其中unix.ctree.c是遍历功能的主要函数的所在地。

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
prefix = /usr

CC=gcc

VERSION=1.7.0
TREE_DEST=tree
BINDIR=${prefix}/bin
MAN=tree.1
MANDIR=${prefix}/man/man1
OBJS=tree.o unix.o html.o xml.o json.o hash.o color.o

# Uncomment options below for your particular OS:

# Linux defaults:
CFLAGS=-ggdb -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
#CFLAGS=-O4 -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
#LDFLAGS=-s

#------------------------------------------------------------

all: tree

tree: $(OBJS)
$(CC) $(LDFLAGS) -o $(TREE_DEST) $(OBJS)

$(OBJS): %.o: %.c tree.h
$(CC) $(CFLAGS) -c -o $@ $<

clean:
if [ -x $(TREE_DEST) ]; then rm $(TREE_DEST); fi
if [ -f tree.o ]; then rm *.o; fi
rm -f *~

install: tree
install -d $(BINDIR)
install -d $(MANDIR)
if [ -e $(TREE_DEST) ]; then \
install $(TREE_DEST) $(BINDIR)/$(TREE_DEST); \
fi
install doc/$(MAN) $(MANDIR)/$(MAN)

distclean:
if [ -f tree.o ]; then rm *.o; fi
rm -f *~


dist: distclean
tar zcf ../tree-$(VERSION).tgz -C .. `cat .tarball`

核心程序-遍历目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//存储文件和文件夹信息的结构体
struct _info {
char *name; /
char *lnk;
bool isdir;
bool issok;
bool isfifo;
bool isexe;
bool orphan;
mode_t mode, lnkmode;
uid_t uid;
gid_t gid;
off_t size;
time_t atime, ctime, mtime;
dev_t dev;
ino_t inode;
#ifdef __EMX__
long attr;
#endif
char *err;
struct _info **child;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
off_t unix_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
{
char *path;
bool nlf = FALSE, colored = FALSE;
long pathsize = 0;
struct _info **dir, **sav;
struct stat sb;
int n, c;

if ((Level >= 0) && (lev > Level)) {
fputc('\n',outfile);
return 0;
}

if (xdev && lev == 0) {
stat(d,&sb);
dev = sb.st_dev;
}

sav = dir = read_dir(d,&n);
if (!dir && n) {
fprintf(outfile," [error opening dir]\n");
return 0;
}
if (!n) {
fputc('\n', outfile);
free_dir(sav);
return 0;
}
if (flimit > 0 && n > flimit) {
fprintf(outfile," [%d entries exceeds filelimit, not opening dir]\n",n);
free_dir(sav);
return 0;
}

if (cmpfunc) qsort(dir,n,sizeof(struct _info *), cmpfunc);
if (lev >= maxdirs-1) {
dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
memset(dirs+(maxdirs-1024), 0, sizeof(int) * 1024);
}
dirs[lev] = 1;
if (!*(dir+1)) dirs[lev] = 2;
fprintf(outfile,"\n");

path = malloc(pathsize=4096);

while(*dir) {
if (!noindent) indent(lev);

fillinfo(path,*dir);
if (path[0] == ' ') {
path[0] = '[';
fprintf(outfile, "%s] ",path);
}

if (colorize) {
if ((*dir)->lnk && linktargetcolor) colored = color((*dir)->lnkmode,(*dir)->name,(*dir)->orphan,FALSE);
else colored = color((*dir)->mode,(*dir)->name,(*dir)->orphan,FALSE);
}

if (fflag) {
if (sizeof(char) * (strlen(d)+strlen((*dir)->name)+2) > pathsize)
path=xrealloc(path,pathsize=(sizeof(char) * (strlen(d)+strlen((*dir)->name)+1024)));
if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
else sprintf(path,"%s/%s",d,(*dir)->name);
} else {
if (sizeof(char) * (strlen((*dir)->name)+1) > pathsize)
path=xrealloc(path,pathsize=(sizeof(char) * (strlen((*dir)->name)+1024)));
sprintf(path,"%s",(*dir)->name);
}

printit(path);

if (colored) fprintf(outfile,"%s",endcode);
if (Fflag && !(*dir)->lnk) {
if ((c = Ftype((*dir)->mode))) fputc(c, outfile);
}

if ((*dir)->lnk) {
fprintf(outfile," -> ");
if (colorize) colored = color((*dir)->lnkmode,(*dir)->lnk,(*dir)->orphan,TRUE);
printit((*dir)->lnk);
if (colored) fprintf(outfile,"%s",endcode);
if (Fflag) {
if ((c = Ftype((*dir)->lnkmode))) fputc(c, outfile);
}
}

if ((*dir)->isdir) {
if ((*dir)->lnk) {
if (lflag && !(xdev && dev != (*dir)->dev)) {
if (findino((*dir)->inode,(*dir)->dev)) {
fprintf(outfile," [recursive, not followed]");
} else {
saveino((*dir)->inode, (*dir)->dev);
if (*(*dir)->lnk == '/')
listdir((*dir)->lnk,dt,ft,lev+1,dev);
else {
if (strlen(d)+strlen((*dir)->lnk)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->lnk);
else sprintf(path,"%s/%s",d,(*dir)->lnk);
listdir(path,dt,ft,lev+1,dev);
}
nlf = TRUE;
}
}
} else if (!(xdev && dev != (*dir)->dev)) {
if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
else sprintf(path,"%s/%s",d,(*dir)->name);
saveino((*dir)->inode, (*dir)->dev);
listdir(path,dt,ft,lev+1,dev);
nlf = TRUE;
}
*dt += 1;
} else *ft += 1;
if (*(dir+1) && !*(dir+2)) dirs[lev] = 2;
if (nlf) nlf = FALSE;
else fprintf(outfile,"\n");
dir++;
}
dirs[lev] = 0;
free(path);
free_dir(sav);
return 0;
}

off_t unix_rlistdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
{
struct _info **dir;
off_t size = 0;
char *err;

dir = getfulltree(d, lev, dev, &size, &err);

memset(dirs, 0, sizeof(int) * maxdirs);

r_listdir(dir, d, dt, ft, lev);

return size;
}

void r_listdir(struct _info **dir, char *d, int *dt, int *ft, u_long lev)
{
char *path;
long pathsize = 0;
struct _info **sav = dir;
bool nlf = FALSE, colored = FALSE;
int c;

if (dir == NULL) return;

dirs[lev] = 1;
if (!*(dir+1)) dirs[lev] = 2;
fprintf(outfile,"\n");

path = malloc(pathsize=4096);

while(*dir) {
if (!noindent) indent(lev);

fillinfo(path,*dir);
if (path[0] == ' ') {
path[0] = '[';
fprintf(outfile, "%s] ",path);
}

if (colorize) {
if ((*dir)->lnk && linktargetcolor) colored = color((*dir)->lnkmode,(*dir)->name,(*dir)->orphan,FALSE);
else colored = color((*dir)->mode,(*dir)->name,(*dir)->orphan,FALSE);
}

if (fflag) {
if (sizeof(char) * (strlen(d)+strlen((*dir)->name)+2) > pathsize)
path=xrealloc(path,pathsize=(sizeof(char) * (strlen(d)+strlen((*dir)->name)+1024)));
if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
else sprintf(path,"%s/%s",d,(*dir)->name);
} else {
if (sizeof(char) * (strlen((*dir)->name)+1) > pathsize)
path=xrealloc(path,pathsize=(sizeof(char) * (strlen((*dir)->name)+1024)));
sprintf(path,"%s",(*dir)->name);
}

printit(path);

if (colored) fprintf(outfile,"%s",endcode);
if (Fflag && !(*dir)->lnk) {
if ((c = Ftype((*dir)->mode))) fputc(c, outfile);
}

if ((*dir)->lnk) {
fprintf(outfile," -> ");
if (colorize) colored = color((*dir)->lnkmode,(*dir)->lnk,(*dir)->orphan,TRUE);
printit((*dir)->lnk);
if (colored) fprintf(outfile,"%s",endcode);
if (Fflag) {
if ((c = Ftype((*dir)->lnkmode))) fputc(c, outfile);
}
}

if ((*dir)->err) {
fprintf(outfile," [%s]", (*dir)->err);
free((*dir)->err);
(*dir)->err = NULL;
}
if ((*dir)->child) {
if (fflag) {
if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
else sprintf(path,"%s/%s",d,(*dir)->name);
}
r_listdir((*dir)->child, fflag? path : NULL, dt, ft, lev+1);
nlf = TRUE;
*dt += 1;
} else {
if ((*dir)->isdir) *dt += 1;
else *ft += 1;
}

if (*(dir+1) && !*(dir+2)) dirs[lev] = 2;
if (nlf) nlf = FALSE;
else fprintf(outfile,"\n");
dir++;
}
dirs[lev] = 0;
free(path);
free_dir(sav);
}

YouCompleteMevim上非常好用的代码自动补全的插件。但是必须自己本机编译他,提高了门槛。
我的环境是ubuntu15.10,照着教程来:

1
2
git submodule add https://github.com/Valloric/YouCompleteMe bundle/YouCompleteMe
git submodule update --init --recursive -- bundle/YouCompleteMe

然后编译:

1
2
cd bundle/YouCompleteMe
./install.sh --clang-completer

但是我编译的时候出现了错误:

1
2
3
4
5
6
7
8
9
10
[ 87%] Building CXX object ycm/CMakeFiles/ycm_core.dir/ClangCompleter/ClangCompleter.cpp.o
In file included from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/BoostParts/boost/type_traits/ice.hpp:15:0,
from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/BoostParts/boost/python/detail/def_helper.hpp:9,
from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/BoostParts/boost/python/class.hpp:29,
from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/BoostParts/boost/python.hpp:18,
from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/ReleaseGil.h:21,
from /home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/ClangCompleter/ClangCompleter.cpp:28:
/home/kai/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/BoostParts/boost/type_traits/detail/ice_or.hpp:17:71: note: #pragma message: NOTE: Use of this header (ice_or.hpp) is deprecated
# pragma message("NOTE: Use of this header (ice_or.hpp) is deprecated")
^

然后一直到最后。
这个是因为在编译boost时,出现了错误。

在这之前,脚本会自动下载clang,然后编译编译boost,而且编译出现了错误。这时我想为什么不用本机上已经安装好的的clangboost呢?

1
2
sudo apt-get instll clang
sudo apt-get install libboost-all-dev

然后执行

1
./install.sh --clang-completer --system-libclang --system-boost

而这一切都在官方文档里有说明,我折腾完才看到,所以认真读说明是很重要的。 :(

使用Pathogen管理插件

推荐这篇教程,使用Pahtogen+Git的方式管理插件和vim配置,方便vim的迁移和团队配置的统一。

自用的vim配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
" 编码
set encoding=utf-8

" 避免以前版本的一些bug和局限
set nocompatible

" 逐步搜索模式,对当前键入的字符进行搜索而不必等待键入完成,/b寻找b开头的。
set incsearch

"自动识别文件类型
" set filetype

" 搜索时高亮显示找到文本
set hlsearch

" 在Insert模式下退格键何时可以删除光标之前的字符。三项内容分别指定了Vim可以删除位于行首的空格,断行,以及开始进入Insert模式之前的位置。
set backspace=indent,eol,start


" 设置tab键为4个空格,设置当行之间交错时使用4个空格
set tabstop=4
set shiftwidth=4

" 匹配模式,左括号匹配右括号
set showmatch

" 在覆盖一个文件之前备份该文件。但是对VMS系统除外,因为该系统已经为文件保存了老的版本。备份文件名由当前文件名加后辍"~"组成。
" if has("vms")
" set nobackup
" else
" set backup
" endif

" 设置冒号命令和搜索命令的命令历史列表的长度。
set history=1000

" 总是在Vim窗口的右下角显示当前光标的行列信息。
set ruler

" 在Vim窗口的右下角显示一个完整的命令已经完成的部分。比如说你键入"2f",Vim就会在你键入下一个要查找的字符之前显示已经键入的"2f"。一旦你接下来再键入一个字符比如"w",那么一个完整的命令"2fw"就会被Vim 执行,同时刚才显示的"2f"也将消失。
set showcmd

" 语法高亮
syntax on

" 显示行号
set nu

" 显示空格和TAB
set list
set listchars=tab:>-,trail:-

" 凸显当前行
set cursorline

" 检测文件类型
filetype on

" 启用鼠标
set mouse=a

" 配色方案
"colorscheme torte

" 以下为插件

" 管理插件的插件pathogen
call pathogen#infect()

" Powerline
set guifont=PowerlineSymbols\ for\ Powerline
set nocompatible
set laststatus=2
set t_Co=256
let g:Powerline_symbols = 'fancy'

" NERDTree
map <F10> :NERDTreeToggle<CR>

" C的编译和运行
map <F5> :call CompileRunGcc()<CR>
func! CompileRunGcc()
exec "w"
exec "!gcc % -o %<"
exec "! ./%<"
endfunc

" C++的编译和运行
map <F6> :call CompileRunGpp()<CR>
func! CompileRunGpp()
exec "w"
exec "!g++ % -o %<"
exec "! ./%<"
endfunc

插件列表

插件名称 项目地址 教程地址
pathogen 项目地址 教程地址
NERDTree 项目地址 教程地址
0%