LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2863|回复: 11

动态生成菜单的程序(配置文件驱动)

[复制链接]
发表于 2003-7-30 13:19:46 | 显示全部楼层 |阅读模式
前两天突然想写个程序练练手,大程序一下又写不了,就写了一个动态生成菜单的小程序,大家看看吧,说不定有点参考价值。
因为是突然想起来写的,所以程序的结构一开始没有好好规划,而且一开始也没有注释,现在有的都是后来加上的。

程序主要有3个模块:
主模块:
文件:main.c
说明:包含main函数,比较简单
menu模块:
文件:menu.h、menu.c
说明:菜单的初始化和删除
display模块:
文件:display.h display.c
说明:显示菜单,接受输入
其他文件:
menudef.h:包含一些常数和结构的定义
my.c:动态库(菜单中用到的函数)
menurc:测试用的配置文件

代码还有一些问题,不是很满意,作为半成品大家讨论一下吧。
 楼主| 发表于 2003-7-30 13:22:32 | 显示全部楼层
menudef.h文件:

  1. #ifndef _MENUDEF_H_
  2. #define _MENUDEF_H_

  3. #define MAXWIDTH 80   /* 菜单最大宽度 */
  4. #define MAXCOUNT 25   /* 最大菜单项数 */
  5. #define MAXLINE  1024 /* 读入行最大长度 */
  6. #define MAXPATH  256  /* 文件名(含路径)最大长度 */

  7. enum{ /* 常数定义 */
  8.   ERR_ITEM = 1, /* MENUITEM::options 错误的菜单项 */
  9.   SUBMENU = 2,  /* MENUITEM::options 子菜单 */
  10.   FUNCTION = 3, /* MENUITEM::options 动态库函数 */
  11.   COMMAND = 4,  /* MENUITEM::options 外部命令 */
  12.   SYSMENU = 0x100, /* MENUITEM::options 系统菜单 */
  13.   SYS_EXIT = 0, /* MENUITEM::options 系统菜单,退出 */
  14.   SYS_ROOT = 1, /* MENUITEM::options 系统菜单,root菜单 */
  15.   SYS_RET = 2, /* MENUITEM::options 系统菜单,上层菜单 */
  16. };

  17. struct MENUITEM{ /* 菜单项 */
  18.   char name[MAXWIDTH + 1];
  19.   char accel;
  20.   void *proc;
  21.   int options;
  22. };

  23. struct MENU{ /* 菜单 */
  24.   char name[MAXWIDTH + 1];
  25.   int count;
  26.   struct MENUITEM item[MAXCOUNT + 2];
  27. };

  28. struct MENUSTACK{ /* 菜单堆栈 */
  29.   struct MENU *stack[100];
  30.   int current;
  31. };

  32. #endif /* _MENUDEF_H_ */
复制代码
 楼主| 发表于 2003-7-30 13:24:14 | 显示全部楼层
main.c文件:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <libgen.h>
  4. #include "menudef.h"
  5. #include "menu.h"
  6. #include "display.h"

  7. extern char rcpath[MAXPATH];
  8. extern char libpath[MAXPATH];

  9. void getexepath(char *);

  10. int
  11. main(void)
  12. {
  13.   char exepath[MAXPATH];
  14.   char menurc[MAXPATH];

  15.   /* 得到程序路径,并将配置文件路径和库文件路径都设为程序路径 */
  16.   getexepath(exepath);
  17.   strncpy(rcpath, exepath, MAXPATH - 1);
  18.   strncpy(libpath, exepath, MAXPATH - 1);

  19.   /* 得到配置文件名(全路径),并初始化菜单结构 */
  20.   strncpy(menurc, rcpath, MAXPATH - 1);
  21.   strcat(menurc, "/menurc");
  22.   if(initmenu(menurc) == -1)
  23.     exit(-1);

  24.   run();

  25.   destroymenu();

  26.   exit(0);
  27. }

  28. void
  29. getexepath(char *path)
  30. {
  31.   char link[MAXPATH], exe[MAXPATH];

  32.   sprintf(link, "/proc/%d/exe", getpid());
  33.   readlink(link, exe, MAXPATH - 1);
  34.   strcpy(path, dirname(exe));
  35. }
复制代码
 楼主| 发表于 2003-7-30 13:29:57 | 显示全部楼层
menu.h menu.c文件:
有些不满意的地方,比如暴露了menusize变量,一开始是因为display模块要用到,后来想想,用一个next函数比较好,还有其他一些地方也不太满意。

  1. #ifndef _MENU_H_
  2. #define _MENU_H_

  3. #include "menudef.h"

  4. int initmenu(char *);
  5. void destroymenu(void);

  6. #endif /* _MENU_H_ */
复制代码

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <dlfcn.h>
  4. #include "menu.h"

  5. struct MENU *menu; /* 全局菜单变量 */
  6. int menusize;      /* 菜单大小(菜单个数) */
  7. char rcpath[MAXPATH];  /* 菜单配置文件路径 */
  8. char libpath[MAXPATH]; /* 动态库路径 */

  9. enum{root, sub}; /* 用于createmenu的参数 */

  10. static struct MENU *createmenu(int);
  11. static void readmenuitems();

  12. /* 读配置文件初始化全局变量menu,分配内存
  13. * 参数:menurc:配置文件名
  14. * 返回值:0:正确;-1:错误
  15. */
  16. int
  17. initmenu(char *menurc)
  18. {
  19.   FILE *fp;
  20.   char line[MAXLINE], *t;
  21.   struct MENU *mp;

  22.   if((fp = fopen(menurc, "r")) == NULL){
  23.     sprintf(line, "cannot open file %s", menurc);
  24.     perror(line);
  25.     return(-1);
  26.   }

  27.   while(fgets(line, MAXLINE - 1, fp)){
  28.     line[strlen(line) - 1] = '\0';
  29.     if((t = strtok(line, " \t")) == NULL)
  30.       continue;
  31.     if(!strcasecmp(t, "rootmenu")){ /* 创建根菜单 */
  32.       if((mp = createmenu(root)) == NULL){
  33.         destroymenu();
  34.         return(-1);
  35.       }
  36.       readmenuitems(mp, fp);
  37.       mp->count++;                  /* 增加exit菜单项 */
  38.       strcpy(mp->item[mp->count - 1].name, "exit");
  39.       mp->item[mp->count - 1].accel = 'x';
  40.       mp->item[mp->count - 1].options = SYSMENU | SYS_EXIT;
  41.     }else if(strcasecmp(t, "submenu") == 0){ /* 创建子菜单 */
  42.       if((mp = createmenu(sub)) == NULL){
  43.         destroymenu();
  44.         return(-1);
  45.       }
  46.       readmenuitems(mp, fp);
  47.       mp->count++;                  /* 增加return和root菜单项 */
  48.       strcpy(mp->item[mp->count - 1].name, "return");
  49.       mp->item[mp->count - 1].accel = 't';
  50.       mp->item[mp->count - 1].options = SYSMENU | SYS_RET;
  51.       mp->count++;
  52.       strcpy(mp->item[mp->count - 1].name, "root");
  53.       mp->item[mp->count - 1].accel = 'r';
  54.       mp->item[mp->count - 1].options = SYSMENU | SYS_ROOT;
  55.     }
  56.   }
  57. }

  58. void
  59. destroymenu(void)
  60. {
  61.   int i, j;

  62.   for(i = 0; i < menusize; i++)
  63.     for(j = 0; j < menu[i].count; j++)
  64.       if(menu[i].item[j].options == SUBMENU || menu[i].item[j].options == COMMAND)
  65.         free(menu[i].item[j].proc);
  66.   free(menu);
  67. }

  68. /* 创建菜单
  69. * 参数:type:菜单类型(root,sub)
  70. * 返回值:新分配菜单的指针;NULL:错误
  71. */
  72. static struct MENU *
  73. createmenu(int type)
  74. {
  75.   char *t;

  76.   if((menu = (struct MENU *)realloc(menu, (menusize + 1) * sizeof(struct MENU))) == NULL){
  77.     fprintf(stderr, "realloc menu error\n");
  78.     return(NULL);
  79.   }

  80.   if(type == root)
  81.     strcpy(menu[menusize].name, "root");
  82.   else if(type == sub){
  83.     t = strtok(NULL, " \t");
  84.     strcpy(menu[menusize].name, t);
  85.   }
  86.   menu[menusize].count = 0;
  87.   menusize++;

  88.   return(&menu[menusize - 1]);
  89. }

  90. /* 读配置文件初始化菜单项
  91. * 参数:m:包含菜单项的菜单;fp:配置文件指针
  92. */
  93. static void
  94. readmenuitems(struct MENU *mp, FILE *fp)
  95. {
  96.   char line[MAXLINE], *name, *type, *menuname, *libname, *funcname, *command;
  97.   int i = -1;
  98.   char accel[] = "1234567890abcdefghijklmno"; /* 为简单起见使用指定快捷键的办法 */

  99.   while(fgets(line, MAXLINE - 1, fp)){
  100.     line[strlen(line) - 1] = '\0';

  101.     if((name = strtok(line, " \t")) == NULL)
  102.       continue;
  103.     if(!strcasecmp(name, "endmenu"))
  104.       break;

  105.     ++i;
  106.     strncpy(mp->item[i].name, name, MAXWIDTH);
  107.     mp->count++;
  108.     mp->item[i].accel = accel[i];

  109.     if((type = strtok(NULL, " \t")) == NULL){
  110.       mp->item[i].options = ERR_ITEM;
  111.       continue;
  112.     }
  113.     if(!strcasecmp(type, "submenu")){
  114.       mp->item[i].options = SUBMENU;
  115.       if((menuname = strtok(NULL, " \t")) == NULL){
  116.         mp->item[i].options = ERR_ITEM;
  117.         continue;
  118.       }
  119.       mp->item[i].proc = (void *)malloc(strlen(menuname) + 1);
  120.       strcpy(mp->item[i].proc, menuname);
  121.     }else if(!strcasecmp(type, "function")){
  122.       void *libc, (*func)();
  123.       char *libfullname;

  124.       mp->item[i].options = FUNCTION;
  125.       if((libname = strtok(NULL, " \t")) == NULL){
  126.         mp->item[i].options = ERR_ITEM;
  127.         continue;
  128.       }
  129.       libfullname = (char *)malloc(strlen(libpath) + strlen(libname) + 2);
  130.       sprintf(libfullname, "%s/%s", libpath, libname);
  131.       if((libc = (void *)dlopen(libfullname, RTLD_LAZY)) == NULL)
  132.         mp->item[i].options = ERR_ITEM;
  133.       else if((funcname = strtok(NULL, " \t")) == NULL)
  134.         mp->item[i].options = ERR_ITEM;
  135.       else if((func = (void (*)())dlsym(libc, funcname)) == NULL)
  136.         mp->item[i].options = ERR_ITEM;
  137.       else
  138.         mp->item[i].proc = func;
  139.       free(libfullname);
  140.     }else if(!strcasecmp(type, "command")){
  141.       mp->item[i].options = COMMAND;
  142.       command = type + strlen(type) + 1; /* 这里缺少错误检查 */
  143.       mp->item[i].proc = (void *)malloc(strlen(command) + 1);
  144.       strcpy(mp->item[i].proc, command);
  145.     }
  146.   }
  147. }
复制代码
 楼主| 发表于 2003-7-30 13:31:32 | 显示全部楼层
display.h display.c文件:

  1. #ifndef _DISPLAY_H_
  2. #define _DISPLAY_H_

  3. #include "menudef.h"

  4. void displaymenu(void);
  5. void run(void);

  6. #endif /* _DISPLAY_H_ */
复制代码

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "display.h"

  4. struct MENUSTACK menustack;
  5. extern struct MENU *menu;
  6. extern int menusize;

  7. static void pushmenu(char *);
  8. static void popmenu(void);
  9. static void rootmenu(void);

  10. void
  11. displaymenu(void)
  12. {
  13.   struct MENU *t;
  14.   int i;

  15.   t = menustack.stack[menustack.current];
  16.   for(i = 0; i < t->count; i++)
  17.     printf("%c) %s\n", t->item[i].accel, t->item[i].name);
  18. }

  19. void
  20. run(void)
  21. {
  22.   char c, s[80];
  23.   int i;
  24.   struct MENU *t;

  25.   menustack.current = -1; /* 因为pushmenu会先menustack.current++,为了把menustack.stack[0] */
  26.   pushmenu("root");       /* 初始化为root,把menustack.current赋值为-1 */
  27.   displaymenu();
  28.   printf("-----------------\n");
  29.   printf("Enter your choice: ");
  30.   while(fgets(s, 79, stdin) != NULL){
  31.     c = s[0];
  32.     t = menustack.stack[menustack.current];
  33.     for(i = 0; i < t->count; i++)
  34.       if(t->item[i].accel == c)
  35.         break;
  36.     if(i == t->count || t->item[i].options == ERR_ITEM){
  37.       printf("\n");
  38.       displaymenu();
  39.       printf("-----------------\n");
  40.       printf("Enter your choice(again): ");
  41.       continue;
  42.     }
  43.     switch(t->item[i].options){
  44.     case SYSMENU | SYS_EXIT:
  45.       return;
  46.       break;
  47.     case SYSMENU | SYS_ROOT:
  48.       rootmenu();
  49.       break;
  50.     case SYSMENU | SYS_RET:
  51.       popmenu();
  52.       break;
  53.     case SUBMENU:
  54.       pushmenu((char *)t->item[i].proc);
  55.       break;
  56.     case FUNCTION:
  57.       printf("**********\n");
  58.       (*(void (*)())t->item[i].proc)();
  59.       printf("**********\n");
  60.       break;
  61.     case COMMAND:
  62.       printf("**********\n");
  63.       system((char *)t->item[i].proc);
  64.       printf("**********\n");
  65.       break;
  66.     }
  67.     printf("\n");
  68.     displaymenu();
  69.     printf("-----------------\n");
  70.     printf("Enter your choice: ");
  71.   }
  72. }

  73. void
  74. pushmenu(char *name)
  75. {
  76.   struct MENU *t;

  77.   for(t = menu; t < menu + menusize; t++)
  78.     if(!strcasecmp(name, t->name)){
  79.       menustack.current++;
  80.       menustack.stack[menustack.current] = t;
  81.       break;
  82.     }
  83. }

  84. void
  85. popmenu(void)
  86. {
  87.   if(menustack.current > 0)
  88.     menustack.current--;
  89. }

  90. static void
  91. rootmenu(void)
  92. {
  93.   menustack.current = 0;
  94. }
复制代码
 楼主| 发表于 2003-7-30 13:32:41 | 显示全部楼层
my.c文件:

  1. #include <stdio.h>
  2. #include <dirent.h>

  3. void
  4. printhw(void)
  5. {
  6.   printf("hello, world!\n");

  7.   return;
  8. }

  9. void
  10. printhelp(void)
  11. {
  12.   printf("help!\n");
  13.   printf("Help!!\n");
  14.   printf("HELP!!!\n");

  15.   return;
  16. }

  17. void
  18. myls(void)
  19. {
  20.   DIR *dp;
  21.   struct dirent *dirp;
  22.   char path[256];

  23.   printf("Please enter a directory(default .): ");
  24.   fgets(path, 255, stdin);
  25.   path[strlen(path) - 1] = '\0';
  26.   if(path[0] == '\0')
  27.     path[0] = '.';
  28.   if((dp = opendir(path)) == NULL){
  29.     fprintf(stderr, "cannot open current directory.\n");
  30.     return;
  31.   }
  32.   while(dirp = readdir(dp))
  33.     printf("%s\n", dirp->d_name);
  34.   closedir(dp);
  35. }
复制代码
 楼主| 发表于 2003-7-30 13:33:34 | 显示全部楼层
menurc文件:

  1. rootmenu
  2.   printhello function libmy.so printhw
  3.   printhelp  function libmy.so printhelp
  4.   cmd        submenu  cmd
  5.   func       submenu  func
  6. endmenu

  7. submenu cmd
  8.   ls   command ls
  9.   ll   command ls -l
  10.   tree command tree
  11.   sh   command bash
  12. endmenu

  13. submenu func
  14.   myls  function libmy.so myls
  15. endmenu
复制代码
 楼主| 发表于 2003-7-30 13:36:27 | 显示全部楼层
编译:
gcc main.c menu.c display.c -ldl -o mn
gcc -shared my.c -o libmy.so
把menurc和mn(可执行文件)放在同一目录下。
 楼主| 发表于 2003-7-30 13:50:36 | 显示全部楼层
关于程序还是有一些其他的考虑。
menu模块的接口比较简单,这是因为把菜单结构全部暴露了出来,考虑一下感觉应该用函数实现,比如getName、action之类的,把数据表示封装起来。
display模块里包含了显示和运行的功能,到底应不应该分成两个模块?
面向对象里的“文档-视图-控制”模型比较有参考价值,这个程序应该可以直接套用一下。

实际这个程序主要是要完善menu模块,只要menu模块写好了display模块应该可以很容易的改一种实现,比如用ncurses实现菜单的显示和程序的输入,现在的实现用的是一个菜单堆栈,如果要用ncuses把菜单显示在屏幕中间的话还可以,如果要实现屏幕顶部的菜单条就不行了,必须换一种实现。
发表于 2003-7-30 13:53:53 | 显示全部楼层
控制台下用curses写菜单界面的程序不少。libinary兄的这个有什么新特点?
我还没有仔细看呢。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表