⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 men.c

📁 操作系统源代码
💻 C
字号:
/* men - menu system for MINIX		Author: Andy Tanenbaum *//* This is a simple menu system for MINIX.  By creating a .menu file and * putting men at the end of a users .profile, when the user logs in, a * menu appears.  The user can select items from the menu by typing short * commands.  The .menu file consists of lines, one per menu entry. * Each line has three parts, separated by semicolons.  The first is the * string to be typed to invoke the menu item.  The second is text to * appear on the screen.  The third is the command sequence to execute * when the item is selected.  The command sequence may need parameters, * so this program allows prompting for them.  At the place where a * parameter must be filled in, the menu writer should put %<prompt>% * where <prompt> tells the user what to fill in.  Many commands produce * some sort of output for the user to inspect (e.g. ls).  To allow this * output to remain on the screen until the user has read it, the special * action: # may be used.  This is a macro for:  * *	echo "Hit RETURN to menu"; read ret * * Here is an example of the .menu file: * * ca:Calendar:cal %month% %year%;echo "Hit RETURN to continue";read ret * cp:Copy file:cp %source file% %destination file% * da:Date:date;sleep 3 * du:Disk Usage:du %directory name%;echo "Hit RETURN to continue";read ret * e:ELLE:elle %file name% * f:Tell fortune:fortune;echo "Hit RETURN to continue";read ret * g:Grep:grep %pattern% %file(s)%;echo "Hit RETURN to continue";read ret * k:Kermit:kermit  * ls:List directory (short):ls -C;echo "Hit RETURN to continue";read ret * ll:List directory (long):ls -l;echo "Hit RETURN to continue";read ret * mi:Mined:mined %file name% * mk:Make file system:mkfs %special% %size or prototype name% * mor:Examine file:more %file name% * mou:Mount floppy:/etc/mount /dev/fd0 %directory to mount on% * mv:Rename a file:mv %old name% %new name% * p:Print current dir name:pwd;echo Hit RETURN to continue";read ret * rec:Recover lost file:recover %file name% * rm:Remove file:rm %file name(s)% * v:vi:/bin/vi * +:RPN calculator:ic * q:Exit menu system:exit * !:Temporary escape to shell:/bin/sh *  */#include <sys/types.h>#include <fcntl.h>#include <termios.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <stdio.h>#define MAX_ITEMS 44		/* max items in all the visible menus */#define EXEC_BUF_SIZE 1024	/* max chars in an executable command */#define MAX_FILE_SIZE 5000	/* # bytes in the .menu file */#define PROMPT_SIZE 256		/* size of the prompt buffer */#define DEFAULT_FILE ".menu"	/* default file where menu is found */#define ENTRIES_PER_COL 11	/* each menu is limited to this many entries */#define LINE_SIZE 80		/* input buffer size */#define SCREEN_WIDTH 80		/* # columns in the screen */#define LINES 23		/* lines on the screen */#define SCREEN_SIZE (LINES * (SCREEN_WIDTH + 1))/* These items define the characters used for building frames around menus. */#define ULCORNER 201#define MIDDLE   205#define URCORNER 187#define LLCORNER 200#define LRCORNER 188#define SINGLE   179#define DOUBLE   186#define LMIDT    204#define RMIDT    185#define DOWN     209#define UP       207#define CROSS    216struct item {  char *code;			/* pointer to code symbol for invocation */  char *desc;			/* pointer to text describing menu item */  char *exec;			/* pointer to commands to execute */} item[MAX_ITEMS];int nitems;			/* number of items in item table */char file_buf[MAX_FILE_SIZE+2];	/* buffer for holding .menu file */char screen[SCREEN_SIZE];	/* screen buffer image */char backspace = '\b';		/* backspace */char newline = '\n';		/* newline */char separator = ':';		/* menu items are separated by this char */char prompt = '%';		/* separator for prompts in commands */char ret = '#';			/* generates special message */char *clr_str = "\033[H\033[0J";	/* ANSI for clear the screen */char *ret_str = "echo '\n\nPlease hit RETURN to go back to the menu';read ret";_PROTOTYPE(int main, (int argc, char **argv));_PROTOTYPE(void read_file, (int argc, char *argv []));_PROTOTYPE(void build_item_table, (void));_PROTOTYPE(void legality_check, (void));_PROTOTYPE(void build_display, (void));_PROTOTYPE(void init_screen, (void));_PROTOTYPE(void build_col, (struct item *ip, int count, int offset, int wid1, int wid2));_PROTOTYPE(void draw, (void));_PROTOTYPE(void barfill, (char *s, int lt, int mid, int rt, int wid1, int wid2));_PROTOTYPE(void execute, (void));_PROTOTYPE(void expand, (char *menu, char *exec));_PROTOTYPE(void clr_screen, (void));_PROTOTYPE(void syntax_err, (char *s));_PROTOTYPE(void code_err, (struct item *ip, struct item *jp));int main(argc, argv)int argc;char *argv[];{  read_file(argc, argv);	/* read in the menu file */  build_item_table();		/* extract info from menu file */  legality_check();		/* check for duplicates etc. */  build_display();  /* Display the menu and get a command to execute. */  while (1) {	draw();	execute();  }  return(0);}void read_file(argc, argv)int argc;char *argv[];{/* Read the menu file into screen. */  char *file_name;  int fd, n;  if (argc > 2) {	fprintf(stderr, "Usage: men [file]\n");	exit(1);  }  file_name = (argc == 2 ? argv[1] : DEFAULT_FILE);  fd = open(file_name, O_RDONLY);  if (fd < 0) {	fprintf(stderr, "Cannot open %s\n", file_name);	exit(1);  }  n = read(fd, file_buf, MAX_FILE_SIZE);  if (n == MAX_FILE_SIZE) {	fprintf(stderr, "%s is too large\n", file_name);	exit(1);  }  file_buf[n+1] = '\n';  file_buf[n+2] = '\0';}void build_item_table(){/* Examine the menu file line by line and build struct item. */  char *p;  struct item *ip;  p = file_buf;  ip = &item[0];  nitems = 0;  while (*p != 0) {	/* Check for too many items. */	if (nitems > MAX_ITEMS) {		fprintf(stderr, "Too many items in the menu (%d)\n", nitems);		exit(1);	}	ip->code = p;		/* p points to the code for this item */	/* Search line for first separator.  It's absence is an error. */	while (*p != separator && *p != '\n') p++;	if (*p == '\n') syntax_err(ip->code);	*p++ = '\0';	/* Search line for second separator.  It's absence is also an error. */	ip->desc = p;		/* p points to the description for this item */	while (*p != separator && *p != '\n') p++;	if (*p == '\n') syntax_err(ip->code);	*p++ = '\0';		/* Search line for line feed.  It cannot be absent (one was added). */	ip->exec = p;	while (*p != '\n') p++;	*p++ = '\0';	/* Move on to next item. */	ip++;	nitems++;  }}void legality_check(){/* It is forbidden to repeat a code in the menu.  It is also forbidden to * have one code be the prefix of another code, i.e., you can't have a * code 'a' and a code 'am' because carriage returns are not used.  As soon * as you type 'a', the first item will be executed. */  size_t n1, n2;  int prompt_count;  char *p;  struct item *ip, *jp;  for (ip = &item[0]; ip < &item[nitems]; ip++) {	for (jp = ip + 1; jp < &item[nitems]; jp++) {		if (strcmp(ip->code, jp->code) == 0) code_err(ip, jp);		n1 = strlen(ip->code);		n2 = strlen(jp->code);		if (strncmp(ip->code, jp->code, n1) == 0) code_err(ip, jp);		if (strncmp(ip->code, jp->code, n2) == 0) code_err(ip, jp);	}	/* If the exec part uses %, they better be balanced. */	prompt_count = 0;	p = ip->exec;	while (*p != '\0') {		if (*p == prompt) prompt_count++;		p++;	}	/* Check to see that it is even. */	if (prompt_count & 01) {		fprintf(stderr,"Unbalanced %% ... %% in: %s\n",ip->exec);		exit(1);	}  }  	}	void build_display(){/* The display is constructed in screen and written out in one blow. */  int wid1, wid2, wid3, cols, k, offset, left, count;  struct item *ip;  /* Determine how wide the columns must be. */  wid1 = 0;			/* code string column width */  wid2 = 0;			/* description column width */  for (ip = &item[0]; ip < &item[nitems]; ip++) {	if (strlen(ip->code) > wid1) wid1 = strlen(ip->code);	if (strlen(ip->desc) > wid2) wid2 = strlen(ip->desc);  }  /* Is there enough room on the screen? */  wid3 = wid1 + wid2 + 9;	/* allow for spaces around strings, etc. */  cols = (nitems + ENTRIES_PER_COL - 1)/ENTRIES_PER_COL;  if (cols * wid3 > SCREEN_WIDTH) {	fprintf(stderr, "Menus are too wide to fit %d of them on the screen\n",								cols);	exit(1);  }  /* There may be several menus next to each other.  Build them separately. */  k = 0;  offset = 0;			/* which column to display the menu in */  left = nitems;		/* how many items left to display */  init_screen();  while (left > 0) {	count = (left < ENTRIES_PER_COL ? left : ENTRIES_PER_COL);	build_col(&item[k], count, offset, wid1, wid2);	left -= count;	k += count;	offset += wid1 + wid2 + 9;  }}void init_screen(){/* Clear the screen buffer */  char *p;  for (p = &screen[0]; p < &screen[SCREEN_SIZE]; p++) *p = ' ';  for (p = &screen[SCREEN_WIDTH]; p < &screen[SCREEN_SIZE]; p +=SCREEN_WIDTH+1)	*p = '\n';}void build_col(ip, count, offset, wid1, wid2)struct item *ip;		/* pointer into item table */int count;			/* number of items to display */int offset;			/* column offset for this menu */int wid1;			/* width of code string */int wid2;			/* width of description screen */{/* Build one vertical menu in screen. */  int k;  char *s, *base;  /* Build top of menu. */  s = &screen[offset];  barfill(s, ULCORNER, DOWN, URCORNER, wid1, wid2);  base = s += SCREEN_WIDTH + 1;  for (k = 0; k < count; k++) {	/* Iterate on each line to be entered. */	s = base;	*s++ = DOUBLE;	*s++ = ' ';	strcpy(s, ip->code);	s = base + wid1 + 3;	*s = SINGLE;	strcpy(base + wid1 + 5, ip->desc);	s = base + wid1 + wid2 + 6;	*s = DOUBLE;	/* Draw the line under the entry.  Last one is different. */	s = base + SCREEN_WIDTH + 1;	if (k != count -1) {		barfill(s, LMIDT, CROSS, RMIDT, wid1, wid2);	} else {		barfill(s, LLCORNER, UP, LRCORNER, wid1, wid2);	}	base += 2 * SCREEN_WIDTH + 2;	ip++;  }}void draw(){/* Draw the menu on the screen. */  char *p;  for (p = &screen[0]; p < &screen[SCREEN_SIZE]; p++)	if (*p == '\0') *p = ' ';  write(1, "\n", 1);  write(1, screen, SCREEN_SIZE);}void barfill(s, lt, mid, rt, wid1, wid2)char *s;			/* where to start */int lt;				/* symbol to use on left */int mid;			/* symbol to use in middle */int rt;				/* symbol to use on right */int wid1;			/* col1 width */int wid2;			/* col2 width */{/* Draw a bar of symbols. */  int i;  *s++ = lt;  for (i = 0; i < wid1 + 2; i++) *s++ = MIDDLE;  *s++ = mid;  for (i = 0; i < wid2 + 2; i++) *s++ = MIDDLE;  *s++ = rt;}  void execute(){/* Get input and execute a command. */  struct termios save, argp;  struct item *ip;  char c[LINE_SIZE], exec_buf[EXEC_BUF_SIZE], *cp;  /* Put terminal in cbreak mode. */  tcgetattr(fileno(stdin), &save);  argp = save;  argp.c_lflag |= ICANON;  /* Read in characters */  cp = &c[0];  while (1) {	tcsetattr(fileno(stdin), TCSANOW, &argp);	read(fileno(stdin), cp, 1);	tcsetattr(fileno(stdin), TCSANOW, &save);	if (*cp == newline) {		cp = &c[0]; /* restart */		continue;	}	if (*cp == backspace && cp > c) {		cp--;		write(1, " \b", 2);		continue;	}		*(cp+1) = 0;	/* Search for a match. */	for (ip = &item[0]; ip < &item[nitems]; ip++) {		if (strcmp(c, ip->code) == 0) {			/* Hit found. */			if (strcmp(ip->exec, "exit") == 0) {				clr_screen();				exit(0);			}			expand(ip->exec, exec_buf);			clr_screen();			system(exec_buf);			return;		}	}	cp++;  }}void expand(menu, exec)char *menu;			/* pointer to exec part of menu item */char *exec;			/* pointer to exec buffer */{/* The exec part of the menu item may have parameters, delimited by %...% * with a prompt in between.  The command is copied to the exec buffer, * and the parameters prompted for and filled in.  The final executable * command is passed by in exec. */  register char *mp, *ep;  int n;  char prompt_buf[PROMPT_SIZE], *pb;  mp = menu;  ep = exec;  *ep = 0;  /* Loop need here.  One iteration per parameter prompted for. */  while (1) {	while (*mp != '\0' && *mp != prompt && *mp != ret) *ep++ = *mp++;	if (*mp == '\0') {		*ep = '\0';		return;	}	/* '#' is a macro for calling "echo xxx;read ret". */	if (*mp == ret) {		pb = ret_str;		while (*pb != '\0') *ep++ = *pb++;		mp++;		continue;	}	/* Start of parameter prompt. Copy it to prompt_buf. */	mp++;	pb = &prompt_buf[0];	while (*mp != prompt) *pb++ = *mp++;	*pb = '\0';	mp++;	*ep = '\0';	/* Prompt the user for the parameter. */	clr_screen();	printf("Please enter:\n\n\t%s\n\nThen hit RETURN\n\n%s",							prompt_buf, exec);	fflush(stdout);	/* Get the reply. */	n = read(0, prompt_buf, PROMPT_SIZE);	prompt_buf[n-1] = '\0';	pb = &prompt_buf[0];	while (*pb != '\0') *ep++ = *pb++;  }		}void clr_screen(){  /* Could use Termcap, but this is simpler and probably faster. */  write(1, clr_str, strlen(clr_str));}void syntax_err(s)char *s;{  fprintf(stderr, "Syntax error in menu file in line:\n%s\n",s);  exit(1);}void code_err(ip, jp)struct item *ip, *jp;{  fprintf(stderr, "The following lines have strings that conflict:\n");  fprintf(stderr, "%s;%s;%s\n",ip->code,ip->desc,ip->exec);  fprintf(stderr, "%s;%s;%s\n",jp->code,jp->desc,jp->exec);  exit(1);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -