下个自主shell蛋
创始人
2024-09-26 17:54:32
0

学个damn!

给我狠狠玩好嘛别卷我啊

知道的是图书馆,不知道的还以为开演唱会呢!

八点啊,那可是八点,才八点!

762a6189b796421095427f0a76cbe942.jpg

不是哥们下午两点半开馆即满人啊我靠?

只能去儿童区学我说不是?

 

别拷打我了我真碎了哥

说说自主shell编写吧(要不然进程控制不白学了)

自主shell首先得有打印命令行提示符吧,命令行提示符构成是这样的:用户名+主机名+当前所处路径,我们都封装成函数:

const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }

分别获取到了对应的字符串,我们还需要一个函数把这些字符串拼接起来,这个功德圆满的函数就是:sprintf!

sprintf是个怎样的函数呢?

sprintf是一个可以向内存空间写入的printf

而snprintf是给定大小的更安全的向内存空间中写入的printf(写到缓冲区)

void MakeCommandLine(char line[], size_t size) { 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, size, "[%s@%s %s]#", username, hostname, cwd); 	fflush(stdout); }

搞完哩, ,,

命令行已经输出了,我们需要获取用户的命令 

而像ls -a -l -i这种命令本质上是字符串嘞,不能直接用scanf获取,因为scanf空格作为分割,但是我们输入命令是不知道有多少空格的输入,按照行获取有木有?

有哇,fgets啊

为何偏偏是它 

 乐意,,,

#include #include #include #include #include  #define SIZE 512  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int main() { 	MakeCommandLine();  	char usercommand[SIZE]; 	char* s = fgets(usercommand, sizeof(usercommand), stdin); 	if (s == NULL) 	{ 		return 1; 	} 	printf("echo:%s\n", usercommand); 	return 0; }

为什么这多输出了一个空行(因为多有了一个\n啊)

为了剔除这个\n,可以这样干:

#include #include #include #include #include #include  #define SIZE 512 #define ZERO '\0'  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int main() { 	MakeCommandLine();  	char usercommand[SIZE]; 	char* s = fgets(usercommand, sizeof(usercommand), stdin); 	if (s == NULL) 	{ 		return 1; 	} 	usercommand[strlen(usercommand) - 1] = ZERO; 	printf("echo:%s\n", usercommand); 	return 0; }

 写成接口就是:

int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }

 接下来需要做的是命令行字符串分割

我们无需再进行手搓函数,有个函数已经提供了分割的功能了:strtok

字符串函数们的使用和模拟实现-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/chestnut_orenge/article/details/135351316 这样赋值,先判断,分割后strtok会返回NULL,刚好让gArgv的最后一个元素是NULL,并且while判断结束

void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }

命令行字符串分割后要执行相应的命令,但是我们不能让该程序执行(代码替换完之后就无了),所以要创建紫禁城帮助我们执行相应的命令

之前学过exec相关函数,我们所获得的命令串都是系统中的,所以默认去环境变量中寻找即可(带p的),又因为获取的是命令串,所以要带v的(容器)

现在大致是这样的:

#include #include #include #include #include #include #include  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32  void Die() {  }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }  char* gArgv[NUM];  void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  int main() { 	MakeCommandLine();  	char usercommand[SIZE]; 	GetUserCommand(usercommand, sizeof(usercommand)); 	 	splitCommand(usercommand, sizeof(usercommand));  	for (int i = 0; gArgv[i]; i++) 	{ 		printf("gArgv[%d]:%s\n", i, gArgv[i]); 	}  	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id,&status,0); 		if (rid > 0) 		{ 			 		} 	} 	return 0; }

但是我们自己编写的这个shell只能运行一次,我们想要让它一直运行执行命令 

于是封装+循环写个简单的shell就好:

#include #include #include #include #include #include #include  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32  void Die() {  }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }  char* gArgv[NUM];  void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  void ExecuteCommand() { 	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id, &status, 0); 		if (rid > 0) 		{  		} 	} }  int main() { 	int quit = 0; 	while (!quit) 	{ 		MakeCommandLine();  		char usercommand[SIZE]; 		GetUserCommand(usercommand, sizeof(usercommand));  		splitCommand(usercommand, sizeof(usercommand));  		ExecuteCommand(); 	} 	return 0; }

大致没问题,但是有个命令跑起来有问题:cd 

 

我们的shell没法进行路径的切换和回退 

每个进程都会记录自己当前所处的路径(父进程有,紫禁城也有)

紫禁城确实是执行了路径切换的命令,但是和父进程没什么关系,而打印命令行是父进程负责的

我们需要让cd交给父进程去执行,也就是所谓的内建命令

所以我们在执行命令前要检查是否是内建命令,由于命令已经被分割,所以直接检查

在获取完用户命令后,进行切换命令需要用到系统调用的接口chdir

 写完了一串代码,会发现cd倒是能用,只不过命令行打印不正常罢了,所以这意味着我们需要对环境变量进行更新

#include #include #include #include #include #include #include  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32  void Die() {  }  const char* Home() { 	const char* home = getenv("HOME"); 	if (home == NULL) 	{ 		return "/"; 	} 	return home; }  void Cd() { 	const char* path = gArgv[1]; 	if (path == NULL) 	{ 		path = Home(); 	} 	chdir(path); }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }  char* gArgv[NUM];  void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  void ExecuteCommand() { 	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id, &status, 0); 		if (rid > 0) 		{  		} 	} }  int CheckBuildin() { 	int yes = 0; 	const char* enter_cmd = gArgv[0]; 	if (strcmp(enter_cmd, "cd") == 0) 	{ 		yes = 1; 		Cd(); 	} 	return yes; }  int main() { 	int quit = 0; 	while (!quit) 	{ 		MakeCommandLine();  		char usercommand[SIZE]; 		int n = GetUserCommand(usercommand, sizeof(usercommand)); 		if (n < 0) 		{ 			return 1; 		}  		splitCommand(usercommand, sizeof(usercommand));  		n = CheckBuildin(); 		if (n) 		{ 			continue; 		}  		ExecuteCommand(); 	} 	return 0; }

 需要注意的是,我们需要刷新环境变量,以保证出来的都是绝对路径

 改完之后除了cd -不支持,其他都支持(cd -需要保存最近一次所处路径)

#include #include #include #include #include #include #include  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32  char cwd[SIZE * 2]; char* gArgv[NUM];  void Die() { 	exit(1); }  const char* Home() { 	const char* home = getenv("HOME"); 	if (home == NULL) 	{ 		return "/"; 	} 	return home; }  void Cd() { 	const char* path = gArgv[1]; 	if (path == NULL) 	{ 		path = Home(); 	} 	chdir(path); 	char temp[SIZE * 2]; 	getcwd(temp, sizeof(temp)); 	snprintf(cwd, sizeof(cwd), "PWD=%s", temp); 	putenv(cwd); }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd(); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, cwd); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }   void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  void ExecuteCommand() { 	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id, &status, 0); 		if (rid > 0) 		{  		} 	} }  int CheckBuildin() { 	int yes = 0; 	const char* enter_cmd = gArgv[0]; 	if (strcmp(enter_cmd, "cd") == 0) 	{ 		yes = 1; 		Cd(); 	} 	return yes; }  int main() { 	int quit = 0; 	while (!quit) 	{ 		MakeCommandLine();  		char usercommand[SIZE]; 		int n = GetUserCommand(usercommand, sizeof(usercommand)); 		if (n < 0) 		{ 			return 1; 		}  		splitCommand(usercommand, sizeof(usercommand));  		n = CheckBuildin(); 		if (n) 		{ 			continue; 		}  		ExecuteCommand(); 	} 	return 0; }

插个眼我很好奇为什么编译不过去

我们自己创建的shell在打印命令行的时候保存的是一串路径,但是正常的shell在保存路径的时候只会保存当前路径,那应该怎么办捏?

就是设个指针让它前移,但是需要做一些特殊处理,在一般情况下打印路径分割符后的内容,在特殊情况下只打印路径分隔符

使用宏是因为要单独对指针进行操作 

用do...while(0)是因为可以形成代码块,且do...while(0)后面可加;

在使用时可当做函数,且可应对分支情况的出现

然后还需要接着判断其他的内建命令

#include #include #include #include #include #include #include  #define SkipPath(p) do{ p += (strlen(p) - 1); while (*p != '/')p--;}while(0)  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32   char cwd[SIZE * 2]; char* gArgv[NUM];  void Die() { 	exit(1); }  const char* Home() { 	const char* home = getenv("HOME"); 	if (home == NULL) 	{ 		return "/"; 	} 	return home; }  void Cd() { 	const char* path = gArgv[1]; 	if (path == NULL) 	{ 		path = Home(); 	} 	chdir(path); 	char temp[SIZE * 2]; 	getcwd(temp, sizeof(temp)); 	snprintf(cwd, sizeof(cwd), "PWD=%s", temp); 	putenv(cwd); }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd();  	SkipPath(cwd); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }   void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  void ExecuteCommand() { 	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id, &status, 0); 		if (rid > 0) 		{  		} 	} }  int CheckBuildin() { 	int yes = 0; 	const char* enter_cmd = gArgv[0]; 	if (strcmp(enter_cmd, "cd") == 0) 	{ 		yes = 1; 		Cd(); 	} 	return yes; }  int main() { 	int quit = 0; 	while (!quit) 	{ 		MakeCommandLine();  		char usercommand[SIZE]; 		int n = GetUserCommand(usercommand, sizeof(usercommand)); 		if (n < 0) 		{ 			return 1; 		}  		splitCommand(usercommand, sizeof(usercommand));  		n = CheckBuildin(); 		if (n) 		{ 			continue; 		}  		ExecuteCommand(); 	} 	return 0; }

判断echo命令

 

 这是记录最近一次的返回值(echo $?)啊麻烦死了自己实现shell

忙活半天只能做个青春版,还不能查历史输入的命令

#include #include #include #include #include #include #include  #define SkipPath(p) do{ p += (strlen(p) - 1); while (*p != '/')p--;}while(0)  #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32  int lastcode = 1; char cwd[SIZE * 2]; char* gArgv[NUM];  void Die() { 	exit(1); }  const char* Home() { 	const char* home = getenv("HOME"); 	if (home == NULL) 	{ 		return "/"; 	} 	return home; }  void Cd() { 	const char* path = gArgv[1]; 	if (path == NULL) 	{ 		path = Home(); 	} 	chdir(path); 	char temp[SIZE * 2]; 	getcwd(temp, sizeof(temp)); 	snprintf(cwd, sizeof(cwd), "PWD=%s", temp); 	putenv(cwd); }  const char* GetUserName() { 	const char* name = getenv("USER"); 	if (name == NULL) 	{ 		return "None"; 	} 	return name; }  const char* GetHostName() { 	const char* hostname = getenv("HOSTNAME"); 	if (hostname == NULL) 	{ 		return "None"; 	} 	return hostname; }  const char* GetCwd() { 	const char* cwd = getenv("PWD"); 	if (cwd == NULL) 	{ 		return "None"; 	} 	return cwd; }  void MakeCommandLine() { 	char line[SIZE]; 	const char* username = GetUserName(); 	const char* hostname = GetHostName(); 	const char* cwd = GetCwd();  	SkipPath(cwd); 	snprintf(line, sizeof(line), "[%s@%s %s]#", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1); 	printf("%s", line); 	fflush(stdout); }  int GetUserCommand(char command[], size_t n) { 	char* s = fgets(command, n, stdin); 	if (s == NULL) 	{ 		return -1; 	} 	command[strlen(command) - 1] = ZERO; 	return strlen(command); }   void splitCommand(char command[], size_t n) { 	gArgv[0] = strtok(command, SEP); 	int index = 1; 	while ((gArgv[index++] = strtok(NULL, SEP))); }  void ExecuteCommand() { 	pid_t id = fork(); 	if (id < 0) 	{ 		Die(); 	} 	else if (id == 0) 	{ 		execvp(gArgv[0], gArgv); 		exit(errno); 	} 	else 	{ 		int status = 0; 		pid_t rid = waitpid(id, &status, 0); 		if (rid > 0) 		{ 			lastcode = WEXITSTATUS(status); 			if (lastcode != 0) 			{ 				printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode); 			} 		} 	} }  int CheckBuildin() { 	int yes = 0; 	const char* enter_cmd = gArgv[0]; 	if (strcmp(enter_cmd, "cd") == 0) 	{ 		yes = 1; 		Cd(); 	} 	else if (strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0) 	{ 		yes = 1; 		printf("%d\n", lastcode); 		lastcode = 0; 	} 	return yes; }  int main() { 	int quit = 0; 	while (!quit) 	{ 		MakeCommandLine();  		char usercommand[SIZE]; 		int n = GetUserCommand(usercommand, sizeof(usercommand)); 		if (n < 0) 		{ 			return 1; 		}  		splitCommand(usercommand, sizeof(usercommand));  		n = CheckBuildin(); 		if (n) 		{ 			continue; 		}  		ExecuteCommand(); 	} 	return 0; }

 在shell中建立环境变量表也是默认创建指针

是一个缓冲区,可以自己申请,环境变量表是char* env[32],而定义开个空间就直接自己设置就好:

char** env = (char*)malloc(sizeof(char*)*32);

 自己写个shell就对它怎样执行命令认识的更加深刻了

相关内容

热门资讯

电视安卓系统能破解吗,破解之路... 电视安卓系统能破解吗?在数字化时代,智能电视已经成为了家庭娱乐的新宠儿。随着安卓系统的普及,越来越多...
苹果怎么重装安卓系统,重装安卓... 你有没有想过,把苹果手机换成安卓系统,是不是会有一种全新的使用体验呢?没错,这就是今天我要跟你分享的...
谷歌安卓系统升级时间,回顾谷歌... 你有没有发现,你的安卓手机最近是不是总在提醒你更新系统呢?没错,谷歌安卓系统升级时间又到了!这个话题...
安卓系统研究团队,探索与创新引... 你知道吗?在科技的世界里,有一个团队正在默默耕耘,他们就是安卓系统研究团队。这个团队可是个宝藏,他们...
安卓系统如何连接carlife... 你有没有想过,你的安卓手机和汽车之间也能来个亲密接触呢?没错,就是那种无缝连接的感觉,让你的手机在车...
800x600安卓系统,800... 亲爱的读者们,你是否曾为拥有一款屏幕尺寸为800x600的安卓手机而感到好奇?这款屏幕尺寸在如今这个...
安卓系统9.0以下中招,探索安... 你知道吗?在安卓系统的大家族里,有一个小秘密正在悄悄流传。那就是,安卓系统9.0以下的小伙伴们,似乎...
安卓系统电脑录屏软件,功能解析... 亲爱的电脑使用者,你是否曾有过这样的需求:想要录制电脑屏幕上的操作过程,以便分享给朋友或者记录重要的...
推荐一款!如何购买金花房卡九酷... 微信游戏中心:九酷大厅/随意玩房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或...
正版授权!怎么买斗牛房卡众亿联... 今 日消息,众亿联盟房卡添加微信33549083 苹果今日发布了 iOS 16.1 正式版更新,简单...
我来教你/牛牛房卡游戏代理精灵... 精灵大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
玩家攻略,金花房卡怎么购买玫瑰... 微信游戏中心:玫瑰大厅房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...
ia攻略/牛牛房卡哪里有卖的毛... 您好!微信毛豆互娱大厅链接获取房卡可以通过以下几种方式购买: 1.微信渠道:(毛豆互娱)大厅介绍:...
科技实测!微信金花房卡怎么弄昆... 今 日消息,昆仑大厅房卡添加微信33549083 苹果今日发布了 iOS 16.1 正式版更新,简单...
正规平台有哪些,牛牛房卡怎么获... 微信游戏中心:超凡联盟房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...
正规平台有哪些,牛牛房卡怎么获... 微信游戏中心:超凡联盟房卡在哪里买打开微信,添加客服微信【88355042】,进入游戏中心或相关小程...
推荐一款!牛牛房卡出售新众亿/... 您好!微信新众亿/皇豪互娱大厅链接获取房卡可以通过以下几种方式购买: 1.微信渠道:(新众亿/皇豪...
正版授权!金花房卡批发价芝麻大... 今 日消息,芝麻大厅房卡添加微信33549083 苹果今日发布了 iOS 16.1 正式版更新,简单...
正版授权!金花房卡批发价芝麻大... 芝麻大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
重大通报,金花房卡怎么购买逍遥... 重大通报,金花房卡怎么购买逍遥大厅/微信链接哪里购买房卡逍遥大厅是一款非常受欢迎的游戏,咨询房/卡添...