├── .gitignore ├── LICENSE ├── README.md ├── chapter1 ├── more01.c └── more02.c ├── chapter2 ├── cp1.c ├── practice │ └── 2.6.c ├── utmplib.c ├── who1.c ├── who2.c └── who3.c ├── chapter3 ├── fileinfo.c ├── ls1.c ├── ls2.c └── ls3.c ├── chapter4 └── spwd.c ├── chapter5 └── write1.c ├── chapter7 ├── bounce.h ├── bounce1d.c ├── bounce2d.c ├── curses_basic │ ├── hello1.c │ ├── hello2.c │ ├── hello3.c │ ├── hello4.c │ └── hello5.c ├── set_ticker.c ├── sigactdemo.c ├── sigdemo3.c └── ticker_demo.c └── chapter9 ├── changeenv.c ├── env.c ├── execute.c ├── makefile ├── smsh.h ├── smsh1.c └── splitline.c /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 庄清惠 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # understanding-unix-linux-programming 2 | Source code for book "Understanding Unix/linux Programming" 3 | -------------------------------------------------------------------------------- /chapter1/more01.c: -------------------------------------------------------------------------------- 1 | /** 2 | * read and print 24 lines then pause for a few special commands 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define PAGELEN 24 10 | #define LINELEN 512 11 | void do_more(FILE*); 12 | int see_more(); 13 | 14 | int main(int ac, char *av[]) { 15 | FILE* fp; 16 | if (ac == 1) { 17 | do_more(stdin); 18 | } else { 19 | while (--ac) { 20 | if ((fp = fopen(*++av, "r")) != NULL) { 21 | do_more(fp); 22 | fclose(fp); 23 | } else { 24 | exit(1); 25 | } 26 | } 27 | } 28 | return 0; 29 | } 30 | 31 | /** 32 | * read PAGELEN lines, then call see_more() for further instructions 33 | */ 34 | void do_more(FILE* fp) { 35 | char line[LINELEN]; 36 | int num_of_lines = 0; 37 | int see_more(), reply; 38 | while (fgets(line, LINELEN, fp)) { 39 | if (num_of_lines == PAGELEN) { 40 | reply = see_more(); 41 | if (reply == 0) 42 | break; 43 | num_of_lines -= reply; 44 | } 45 | if (fputs(line, stdout) == EOF) 46 | exit(1); 47 | num_of_lines++; 48 | } 49 | } 50 | 51 | /** 52 | * print message, wait for response, return # of lines to advance 53 | * q means no, space means yes, CR means one line 54 | */ 55 | int see_more() { 56 | int c; 57 | printf("\033[7m more? \033[m"); 58 | while ((c = getchar()) != EOF) { 59 | if (c == 'q') 60 | return 0; 61 | if (c == ' ') 62 | return PAGELEN; 63 | if (c == '\n') 64 | return 1; 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /chapter1/more02.c: -------------------------------------------------------------------------------- 1 | /** 2 | * read and print 24 lines then pause for a few special commands 3 | * feature of version 0.2: reads from /dev/tty for commands 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define PAGELEN 24 11 | #define LINELEN 512 12 | void do_more(FILE*); 13 | int see_more(FILE*); 14 | 15 | int main(int ac, char *av[]) { 16 | FILE* fp; 17 | if (ac == 1) { 18 | do_more(stdin); 19 | } else { 20 | while (--ac) { 21 | if ((fp = fopen(*++av, "r")) != NULL) { 22 | do_more(fp); 23 | fclose(fp); 24 | } else { 25 | exit(1); 26 | } 27 | } 28 | } 29 | return 0; 30 | } 31 | 32 | /** 33 | * read PAGELEN lines, then call see_more() for further instructions 34 | */ 35 | void do_more(FILE* fp) { 36 | char line[LINELEN]; 37 | int num_of_lines = 0; 38 | int reply; 39 | 40 | FILE* fp_tty = fopen("/dev/tty", "r"); 41 | if (fp_tty == NULL) 42 | exit(1); 43 | 44 | while (fgets(line, LINELEN, fp)) { 45 | if (num_of_lines == PAGELEN) { 46 | reply = see_more(fp_tty); 47 | if (reply == 0) 48 | break; 49 | num_of_lines -= reply; 50 | } 51 | if (fputs(line, stdout) == EOF) 52 | exit(1); 53 | num_of_lines++; 54 | } 55 | } 56 | 57 | /** 58 | * print message, wait for response, return # of lines to advance 59 | * q means no, space means yes, CR means one line 60 | */ 61 | int see_more(FILE* cmd) { 62 | int c; 63 | printf("\033[7m more? \033[m"); 64 | while ((c = getc(cmd)) != EOF) { 65 | if (c == 'q') 66 | return 0; 67 | if (c == ' ') 68 | return PAGELEN; 69 | if (c == '\n') 70 | return 1; 71 | } 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /chapter2/cp1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * uses read and write with tunable buffer size 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUFFERSIZE 4096 10 | #define COPYMODE 0644 11 | 12 | void oops(char *, char *); 13 | 14 | int main(int argc, char *argv[]) { 15 | int in_fd, out_fd, n_chars; 16 | char buf[BUFFERSIZE]; 17 | 18 | if (argc != 3) { 19 | fprintf(stderr, "usage: %s source destination\n", *argv); 20 | exit(1); 21 | } 22 | 23 | if ((in_fd = open(argv[1], O_RDONLY)) == -1) 24 | oops("Cannot open ", argv[1]); 25 | 26 | if ((out_fd = creat(argv[2], COPYMODE)) == -1) 27 | oops("Cannot create ", argv[2]); 28 | 29 | // copy files 30 | while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0) { 31 | if (write(out_fd, buf, n_chars) != n_chars) 32 | oops("Write error to ", argv[2]); 33 | } 34 | 35 | if (n_chars == -1) 36 | oops("Read error from ", argv[1]); 37 | 38 | if (close(in_fd) == -1 || close(out_fd) == -1) 39 | oops("Error closing files", ""); 40 | 41 | return 0; 42 | } 43 | 44 | void oops(char *s1, char *s2) { 45 | fprintf(stderr, "Error: %s ", s1); 46 | perror(s2); 47 | exit(1); 48 | } 49 | -------------------------------------------------------------------------------- /chapter2/practice/2.6.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define BUFFERSIZE 20 7 | #define MODE 0664 8 | 9 | int main() { 10 | int fd1_count; 11 | int fd2_count; 12 | int fd3_count; 13 | char buf1[BUFFERSIZE]; 14 | char buf3[BUFFERSIZE]; 15 | 16 | const char file_path[] = "./test"; 17 | int in_fd; 18 | 19 | if ((in_fd = creat(file_path, MODE)) == -1) { 20 | perror("Cannot create test file"); 21 | exit(1); 22 | } 23 | 24 | // operation1 25 | if ((fd1_count = read(in_fd, buf1, BUFFERSIZE)) == -1) { 26 | perror("fail to read in 1"); 27 | } 28 | printf("%s in 1\n", buf1); 29 | 30 | // operation2 31 | if ((fd2_count = write(in_fd, "testing...", BUFFERSIZE)) == -1) { 32 | perror("fail to write in 2"); 33 | } 34 | 35 | // operation3 36 | if ((fd3_count = read(in_fd, buf3, BUFFERSIZE)) == -1) { 37 | perror("Cannot open in 3"); 38 | } 39 | printf("%s in 3\n", buf3); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /chapter2/utmplib.c: -------------------------------------------------------------------------------- 1 | /** 2 | * utmplib.h - function to buffer read from utmp file 3 | * functions are 4 | * utmp_open(filename) - open file 5 | * return -1 on error 6 | * utmp_next() - return pointer to next struct 7 | * return NULL on eof 8 | * ump_close() - close file 9 | * 10 | * read NRECS per read and then doles them out from the buffer 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define NRECS 16 19 | #define NULLUT ((struct utmp *)NULL) 20 | #define UTSIZE (sizeof(struct utmp)) 21 | 22 | static char utmpbuf[NRECS * UTSIZE]; // storage 23 | static int num_recs; // num stored 24 | static int cur_rec; // next to go 25 | static int fd_utmp = -1; // read from 26 | 27 | int utmp_open(char *filename) { 28 | fd_utmp = open(filename, O_RDONLY); 29 | cur_rec = num_recs = 0; 30 | return fd_utmp; 31 | } 32 | 33 | int utmp_reload() { 34 | int amt_read; 35 | amt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE); 36 | num_recs = amt_read / UTSIZE; 37 | cur_rec = 0; 38 | return num_recs; 39 | } 40 | 41 | struct utmp * utmp_next() { 42 | struct utmp *recp; 43 | if (fd_utmp == -1) 44 | return NULLUT; 45 | if (cur_rec == num_recs && utmp_reload() == 0) 46 | return NULLUT; 47 | 48 | recp = (struct utmp *)&utmpbuf[cur_rec * UTSIZE]; 49 | cur_rec++; 50 | return recp; 51 | } 52 | 53 | void utmp_close() { 54 | if (fd_utmp != -1) 55 | close(fd_utmp); 56 | } 57 | -------------------------------------------------------------------------------- /chapter2/who1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * open, read UTMP file, and show 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define SHOWHOST 12 | 13 | void show_info(struct utmp*); 14 | 15 | int main() { 16 | struct utmp current_record; 17 | int utmpfd; 18 | int reclen = sizeof(current_record); 19 | 20 | if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1) { 21 | perror(UTMP_FILE); 22 | exit(1); 23 | } 24 | 25 | while (read(utmpfd, ¤t_record, reclen) == reclen) 26 | show_info(¤t_record); 27 | close(utmpfd); 28 | return 0; 29 | } 30 | 31 | /** 32 | * display cotents of the utmp struct in human readable form 33 | * *note* these sizes should not be hardwired 34 | */ 35 | void show_info(struct utmp* utbufp) { 36 | printf("%-8.8s", utbufp->ut_name); 37 | printf(" "); 38 | printf("%-8.8s", utbufp->ut_line); 39 | printf(" "); 40 | printf("%10d", utbufp->ut_time); 41 | printf(" "); 42 | #ifdef SHOWHOST 43 | printf("(%s)", utbufp->ut_host); 44 | #endif 45 | printf("\n"); 46 | } 47 | -------------------------------------------------------------------------------- /chapter2/who2.c: -------------------------------------------------------------------------------- 1 | /** 2 | * open, read UTMP file, and show 3 | * suppresses empty records 4 | * formats time nicely 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define SHOWHOST 15 | void show_time(long); 16 | void show_info(struct utmp*); 17 | 18 | int main() { 19 | struct utmp current_record; 20 | int utmpfd; 21 | int reclen = sizeof(current_record); 22 | 23 | if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1) { 24 | perror(UTMP_FILE); 25 | exit(1); 26 | } 27 | 28 | while (read(utmpfd, ¤t_record, reclen) == reclen) 29 | show_info(¤t_record); 30 | close(utmpfd); 31 | return 0; 32 | } 33 | 34 | /** 35 | * display cotents of the utmp struct in human readable form 36 | * *note* these sizes should not be hardwired 37 | */ 38 | void show_info(struct utmp* utbufp) { 39 | if (utbufp->ut_type != USER_PROCESS) // user only 40 | return; 41 | 42 | printf("%-8.8s", utbufp->ut_name); 43 | printf(" "); 44 | printf("%-8.8s", utbufp->ut_line); 45 | printf(" "); 46 | show_time(utbufp->ut_time); 47 | #ifdef SHOWHOST 48 | if (utbufp->ut_host[0] != '\0') 49 | printf("(%s)", utbufp->ut_host); 50 | #endif 51 | printf("\n"); 52 | } 53 | 54 | /** 55 | * display time in a format fit for human consumption 56 | * use ctime to build a string then picks pars out of it 57 | * Node: %12.12s prints a string 12 chars wide and LIMITS 58 | * it to 12 chars 59 | */ 60 | void show_time(long timeval) { 61 | char *cp; 62 | cp = ctime(&timeval); // looks like: Mon Feb 4 00:46:40 EST 1991 63 | printf("%12.12s", cp + 4); 64 | } 65 | -------------------------------------------------------------------------------- /chapter2/who3.c: -------------------------------------------------------------------------------- 1 | /** 2 | * command 'who' with buffered reads 3 | * - open, read UTMP file, and show 4 | * - suppresses empty records 5 | * - formats time nicely 6 | * - buffers input (using utmplib) 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "utmplib.c" 16 | 17 | #define SHOWHOST 18 | void show_time(long); 19 | void show_info(struct utmp*); 20 | 21 | int main() { 22 | struct utmp * utbufp; 23 | 24 | if (utmp_open(UTMP_FILE) == -1) { 25 | perror(UTMP_FILE); 26 | exit(1); 27 | } 28 | 29 | while ((utbufp = utmp_next()) != ((struct utmp *)NULL)) 30 | show_info(utbufp); 31 | utmp_close(); 32 | return 0; 33 | } 34 | 35 | /** 36 | * display cotents of the utmp struct in human readable form 37 | * *note* these sizes should not be hardwired 38 | */ 39 | void show_info(struct utmp* utbufp) { 40 | if (utbufp->ut_type != USER_PROCESS) // user only 41 | return; 42 | 43 | printf("%-8.8s", utbufp->ut_name); 44 | printf(" "); 45 | printf("%-8.8s", utbufp->ut_line); 46 | printf(" "); 47 | show_time(utbufp->ut_time); 48 | #ifdef SHOWHOST 49 | if (utbufp->ut_host[0] != '\0') 50 | printf("(%s)", utbufp->ut_host); 51 | #endif 52 | printf("\n"); 53 | } 54 | 55 | /** 56 | * display time in a format fit for human consumption 57 | * use ctime to build a string then picks pars out of it 58 | * Node: %12.12s prints a string 12 chars wide and LIMITS 59 | * it to 12 chars 60 | */ 61 | void show_time(long timeval) { 62 | char *cp; 63 | cp = ctime(&timeval); // looks like: Mon Feb 4 00:46:40 EST 1991 64 | printf("%12.12s", cp + 4); 65 | } 66 | -------------------------------------------------------------------------------- /chapter3/fileinfo.c: -------------------------------------------------------------------------------- 1 | /** 2 | * - use stat() to obtain and print file properties 3 | * - some members are just numbers 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | void show_stat_info(char *fname, struct stat *buf); 10 | 11 | int main(int argc, char *argv[]) { 12 | struct stat info; 13 | 14 | if (argc > 1) { 15 | if (stat(argv[1], &info) != -1) { 16 | show_stat_info(argv[1], &info); 17 | return 0; 18 | } 19 | } else { 20 | perror(argv[1]); 21 | } 22 | return 1; 23 | } 24 | 25 | /** 26 | * display some info from stat in a name = value format 27 | */ 28 | void show_stat_info(char *fname, struct stat *buf) { 29 | printf(" mode: %o\n", buf->st_mode); 30 | printf(" links: %lu\n", buf->st_nlink); 31 | printf(" user: %d\n", buf->st_uid); 32 | printf(" group: %d\n", buf->st_gid); 33 | printf(" size: %ld\n", buf->st_size); 34 | printf(" modtime: %ld\n", buf->st_mtime); 35 | printf(" name: %s\n", fname); 36 | } 37 | -------------------------------------------------------------------------------- /chapter3/ls1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose list contents of directory or directories 3 | * action if no args, user . else list files args 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void do_ls(char[]); 11 | 12 | int main(int argc, char *argv[]) { 13 | if (argc == 1) { 14 | do_ls("."); 15 | } else { 16 | while (--argc) { 17 | printf("%s:\n", *++argv); 18 | do_ls(*argv); 19 | } 20 | } 21 | } 22 | 23 | /** 24 | * list files in directory called dirname 25 | */ 26 | void do_ls(char dirname[]) { 27 | DIR *dir_ptr; 28 | struct dirent *direntp; 29 | 30 | if ((dir_ptr = opendir(dirname)) == NULL) { 31 | fprintf(stderr, "ls1: cannot open %s \n", dirname); 32 | } else { 33 | while ((direntp = readdir(dir_ptr)) != NULL) { 34 | printf("%s\n", direntp->d_name); 35 | } 36 | closedir(dir_ptr); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter3/ls2.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose list contents of directory or directories 3 | * action if no args, user . else list files args 4 | * note: users stat and pwd.h and grp.h 5 | * BUG: try ls2 /tmp 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void do_ls(char[]); 16 | void do_stat(char *); 17 | void show_file_info(char *, struct stat *); 18 | void mode_to_letters(int, char []); 19 | char * uid_to_name(uid_t); 20 | char * gid_to_name(gid_t); 21 | 22 | int main(int argc, char *argv[]) { 23 | if (argc == 1) { 24 | do_ls("."); 25 | } else { 26 | while (--argc) { 27 | printf("%s:\n", *++argv); 28 | do_ls(*argv); 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * list files in directory called dirname 35 | */ 36 | void do_ls(char dirname[]) { 37 | DIR *dir_ptr; 38 | struct dirent *direntp; 39 | 40 | if ((dir_ptr = opendir(dirname)) == NULL) { 41 | fprintf(stderr, "ls2: cannot open %s \n", dirname); 42 | } else { 43 | while ((direntp = readdir(dir_ptr)) != NULL) { 44 | do_stat(direntp->d_name); 45 | } 46 | closedir(dir_ptr); 47 | } 48 | } 49 | 50 | void do_stat(char *filename) { 51 | struct stat info; 52 | if (stat(filename, &info) == -1) { 53 | perror(filename); 54 | } else { 55 | show_file_info(filename, &info); 56 | } 57 | } 58 | 59 | /** 60 | * display the info about filename. 61 | * the info is stored in struct at * info_p 62 | */ 63 | void show_file_info(char *filename, struct stat * info_p) { 64 | char modestr[11]; 65 | 66 | mode_to_letters(info_p->st_mode, modestr); 67 | 68 | printf("%s", modestr); 69 | printf("%4d ", (int)info_p->st_nlink); 70 | printf("%-8s ", uid_to_name(info_p->st_uid)); 71 | printf("%-8s ", gid_to_name(info_p->st_gid)); 72 | printf("%8ld ", (long)info_p->st_size); 73 | printf("%.12s ", 4 + ctime(&info_p->st_mtime)); 74 | printf("%s\n", filename); 75 | } 76 | 77 | /** 78 | * utility functions 79 | */ 80 | void mode_to_letters(int mode, char str[]) { 81 | strcpy(str, "----------"); 82 | if (S_ISDIR(mode)) str[0] = 'd'; 83 | if (S_ISCHR(mode)) str[0] = 'c'; 84 | if (S_ISBLK(mode)) str[0] = 'b'; 85 | 86 | if (mode & S_IRUSR) str[1] = 'r'; // 3 bits for user 87 | if (mode & S_IWUSR) str[2] = 'w'; 88 | if (mode & S_IXUSR) str[3] = 'x'; 89 | 90 | if (mode & S_IRGRP) str[4] = 'r'; // 3 bits for group 91 | if (mode & S_IWGRP) str[5] = 'w'; 92 | if (mode & S_IXGRP) str[6] = 'x'; 93 | 94 | if (mode & S_IROTH) str[7] = 'r'; // 3 bits for other 95 | if (mode & S_IWOTH) str[8] = 'w'; 96 | if (mode & S_IXOTH) str[9] = 'x'; 97 | } 98 | 99 | 100 | #include 101 | 102 | /** 103 | * returns pointer to username associated with uid, uses getpw() 104 | */ 105 | char * uid_to_name(uid_t uid) { 106 | struct passwd * getpwuid(), *pw_ptr; 107 | static char numstr[10]; 108 | 109 | if ((pw_ptr = getpwuid(uid)) == NULL) { 110 | sprintf(numstr, "%d", uid); 111 | return numstr; 112 | } else { 113 | return pw_ptr->pw_name; 114 | } 115 | } 116 | 117 | #include 118 | 119 | /** 120 | * returns pointer to group number gid, used getgrgid(3) 121 | */ 122 | char * gid_to_name(gid_t gid) { 123 | struct group * grp_ptr; 124 | static char numstr[10]; 125 | if ((grp_ptr = getgrgid(gid)) == NULL) { 126 | sprintf(numstr, "%d", gid); 127 | return numstr; 128 | } else { 129 | return grp_ptr->gr_name; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /chapter3/ls3.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose list contents of directory or directories 3 | * action if no args, use . else list files args 4 | * note: users stat and pwd.h and grp.h 5 | * BUG: try ls2 /tmp 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAXLEN 1000 18 | #define MAXNAMELEN 1000 19 | 20 | enum FILENAME {A,B}; // specify the file 21 | 22 | void do_ls(char[]); 23 | void do_stat(char *); 24 | int filename_cmp(const void *, const void *); 25 | void string_unified(char *, enum FILENAME); 26 | void show_file_info(char *, struct stat *); 27 | void mode_to_letters(int, char []); 28 | char * uid_to_name(uid_t); 29 | char * gid_to_name(gid_t); 30 | 31 | struct dirent *sorted_file_set[MAXLEN]; 32 | char filenamea[MAXNAMELEN]; // unified filename 33 | char filenameb[MAXNAMELEN]; 34 | 35 | int main(int argc, char *argv[]) { 36 | if (argc == 1) { 37 | do_ls("."); 38 | } else { 39 | while (--argc) { 40 | printf("%s:\n", *++argv); 41 | do_ls(*argv); 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * list files in directory called dirname 48 | */ 49 | void do_ls(char dirname[]) { 50 | DIR *dir_ptr; 51 | struct dirent *direntp; 52 | int index = 0; 53 | 54 | if ((dir_ptr = opendir(dirname)) == NULL) { 55 | fprintf(stderr, "ls2: cannot open %s \n", dirname); 56 | } else { 57 | while (index < MAXLEN && (direntp = readdir(dir_ptr)) != NULL) { 58 | sorted_file_set[index++] = direntp; 59 | } 60 | printf("total %d\n", index); 61 | // sort by filename 62 | qsort(sorted_file_set, index, sizeof(sorted_file_set[0]), filename_cmp); 63 | 64 | // display the info about sorted filename 65 | for (int i = 0; i < index; ++i) { 66 | do_stat(sorted_file_set[i]->d_name); 67 | } 68 | 69 | closedir(dir_ptr); 70 | } 71 | } 72 | 73 | void do_stat(char *filename) { 74 | struct stat info; 75 | if (stat(filename, &info) == -1) { 76 | perror(filename); 77 | } else { 78 | show_file_info(filename, &info); 79 | } 80 | } 81 | 82 | /** 83 | * display the info about filename. 84 | * the info is stored in struct at * info_p 85 | */ 86 | void show_file_info(char *filename, struct stat * info_p) { 87 | char modestr[11]; 88 | 89 | mode_to_letters(info_p->st_mode, modestr); 90 | 91 | printf("%s", modestr); 92 | printf("%4d ", (int)info_p->st_nlink); 93 | printf("%-8s ", uid_to_name(info_p->st_uid)); 94 | printf("%-8s ", gid_to_name(info_p->st_gid)); 95 | printf("%8ld ", (long)info_p->st_size); 96 | printf("%.12s ", 4 + ctime(&info_p->st_mtime)); 97 | printf("%s\n", filename); 98 | } 99 | 100 | /** 101 | * utility functions 102 | */ 103 | int filename_cmp(const void *filepa, const void *filepb) { 104 | char *originnamea, *originnameb; 105 | originnamea = (*(struct dirent**)filepa)->d_name; 106 | originnameb = (*(struct dirent**)filepb)->d_name; 107 | 108 | string_unified(originnamea, A); 109 | string_unified(originnameb, B); 110 | 111 | return strcmp(filenamea, filenameb); 112 | } 113 | 114 | /** 115 | * example: ABcd => .abcd 116 | */ 117 | void string_unified(char *str, enum FILENAME name) { 118 | char *filename = NULL; 119 | if (name == A) { 120 | filename = filenamea; 121 | } else if (name == B) { 122 | filename = filenameb; 123 | } 124 | 125 | memset(filename, 0, MAXNAMELEN); 126 | if (str[0] != '.') 127 | filename[0] = '.'; 128 | 129 | strcat(filename, str); 130 | for (int i = strlen(filename) - 1; i >= 0; --i) { 131 | filename[i] = tolower(filename[i]); 132 | } 133 | } 134 | 135 | void mode_to_letters(int mode, char str[]) { 136 | strcpy(str, "----------"); 137 | if (S_ISDIR(mode)) str[0] = 'd'; 138 | if (S_ISCHR(mode)) str[0] = 'c'; 139 | if (S_ISBLK(mode)) str[0] = 'b'; 140 | 141 | if (mode & S_IRUSR) str[1] = 'r'; // 3 bits for user 142 | if (mode & S_IWUSR) str[2] = 'w'; 143 | if (mode & S_IXUSR) str[3] = 'x'; 144 | 145 | if (mode & S_IRGRP) str[4] = 'r'; // 3 bits for group 146 | if (mode & S_IWGRP) str[5] = 'w'; 147 | if (mode & S_IXGRP) str[6] = 'x'; 148 | 149 | if (mode & S_IROTH) str[7] = 'r'; // 3 bits for other 150 | if (mode & S_IWOTH) str[8] = 'w'; 151 | if (mode & S_IXOTH) str[9] = 'x'; 152 | } 153 | 154 | 155 | #include 156 | 157 | /** 158 | * returns pointer to username associated with uid, uses getpw() 159 | */ 160 | char * uid_to_name(uid_t uid) { 161 | struct passwd * getpwuid(), *pw_ptr; 162 | static char numstr[10]; 163 | 164 | if ((pw_ptr = getpwuid(uid)) == NULL) { 165 | sprintf(numstr, "%d", uid); 166 | return numstr; 167 | } else { 168 | return pw_ptr->pw_name; 169 | } 170 | } 171 | 172 | #include 173 | 174 | /** 175 | * returns pointer to group number gid, used getgrgid(3) 176 | */ 177 | char * gid_to_name(gid_t gid) { 178 | struct group * grp_ptr; 179 | static char numstr[10]; 180 | if ((grp_ptr = getgrgid(gid)) == NULL) { 181 | sprintf(numstr, "%d", gid); 182 | return numstr; 183 | } else { 184 | return grp_ptr->gr_name; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /chapter4/spwd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * starts in current directory and recursively 3 | * climbs to the root of filesystem, prints top part 4 | * then prints current part 5 | * - use readdir() to get info about each thing 6 | * - bug: prints an empty string if run from "/" 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | ino_t get_inode(char *); 18 | void print_path_to(ino_t); 19 | void inum_to_name(ino_t, char *, int); 20 | 21 | int main() { 22 | print_path_to(get_inode(".")); 23 | putchar('\n'); 24 | return 0; 25 | } 26 | 27 | /** 28 | * print path leading down to an object with this node 29 | * kindof recursive 30 | */ 31 | void print_path_to(ino_t this_inode) { 32 | ino_t my_inode; 33 | char its_name[BUFSIZ]; 34 | if (get_inode("..") != this_inode) { 35 | printf("here\n"); 36 | chdir(".."); 37 | inum_to_name(this_inode, its_name, BUFSIZ); 38 | my_inode = get_inode("."); 39 | print_path_to(my_inode); 40 | printf("/%s", its_name); 41 | } 42 | } 43 | 44 | /** 45 | * looks through current directory for a file with this node 46 | * number and copies its name into namebuf 47 | */ 48 | void inum_to_name(ino_t inode_to_find, char *namebuf, int buflen) { 49 | DIR *dir_ptr; 50 | struct dirent * direntp; 51 | dir_ptr = opendir("."); 52 | if (dir_ptr) { 53 | perror("."); 54 | exit(1); 55 | } 56 | 57 | while ((direntp = readdir(dir_ptr))) { 58 | if (direntp->d_ino == inode_to_find) { 59 | strncpy(namebuf, direntp->d_name, buflen); 60 | namebuf[buflen-1] = '\0'; 61 | closedir(dir_ptr); 62 | return; 63 | } 64 | } 65 | 66 | fprintf(stderr, "error looking for inum %lu\n", inode_to_find); 67 | exit(1); 68 | } 69 | 70 | /** 71 | * return inode number of the file 72 | */ 73 | ino_t get_inode(char *fname) { 74 | struct stat info; 75 | if (stat(fname, &info) == -1) { 76 | fprintf(stderr, "Cannot stat"); 77 | perror(fname); 78 | exit(1); 79 | } 80 | return info.st_ino; 81 | } 82 | -------------------------------------------------------------------------------- /chapter5/write1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose: send messages to another terminal 3 | * method: open the other terminal for output then 4 | * copy from stdin to that terminal 5 | * shows: a terminal is just a file supporting regular i/o 6 | * usage: write0 ttyname 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc, char *argv[]) { 16 | int fd; 17 | char buf[BUFSIZ]; 18 | // check args 19 | if (argc != 2) { 20 | fprintf(stderr, "usage: write0 ttyname\n"); 21 | exit(1); 22 | } 23 | 24 | // open devices 25 | fd = open(argv[1], O_WRONLY); 26 | if (fd == -1) { 27 | perror(argv[1]); 28 | exit(1); 29 | } 30 | 31 | // loop until EOF on input 32 | while (fgets(buf, BUFSIZ, stdin) != NULL) { 33 | if (write(fd, buf, strlen(buf)) == -1) 34 | break; 35 | } 36 | close(fd); 37 | } 38 | -------------------------------------------------------------------------------- /chapter7/bounce.h: -------------------------------------------------------------------------------- 1 | // some setting for the game 2 | #ifndef BOUNCE_H 3 | #define BOUNCE_H 4 | #define BLANK ' ' 5 | #define DFL_SYMBOL 'O' 6 | #define TOP_ROW 5 7 | #define BOT_ROW 20 8 | #define LEFT_EDGE 10 9 | #define RIGHT_EDGE 70 10 | #define X_INIT 10 11 | #define Y_INIT 10 12 | #define TICKS_PER_SEC 50 13 | 14 | #define X_TTM 5 15 | #define Y_TTM 8 16 | 17 | // the ping pong ball 18 | 19 | struct ppball { 20 | int y_pos, x_pos, 21 | y_ttm, x_ttm, 22 | y_ttg, x_ttg, 23 | y_dir, x_dir; 24 | char symbol; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /chapter7/bounce1d.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose animation with user controlled speed and direction 3 | * note the handler does the animation 4 | * the main program reads keyboard input 5 | * compile gcc bounce1d.c set_ticker.c -l curses -o bounce1d 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MESSAGE "hello" 14 | #define BLANK " " 15 | 16 | int row; 17 | int col; 18 | int dir; 19 | 20 | int main() { 21 | int delay; // bigger => slower 22 | int ndelay; // new delay 23 | int c; // user input 24 | void move_msg(int); 25 | int set_ticker(int); 26 | initscr(); 27 | crmode(); 28 | noecho(); 29 | clear(); 30 | 31 | row = 10; 32 | col = 0; 33 | dir = 1; 34 | delay = 200; // 200ms 35 | 36 | move(row, col); 37 | addstr(MESSAGE); 38 | signal(SIGALRM, move_msg); 39 | set_ticker(delay); 40 | 41 | while (1) { 42 | ndelay = 0; 43 | c = getch(); 44 | if (c == 'Q') break; 45 | if (c == ' ') dir = -dir; 46 | if (c == 'f' && delay > 2) ndelay = delay / 2; 47 | if (c == 's') ndelay = delay * 2; 48 | if (ndelay > 0) 49 | set_ticker(delay = ndelay); 50 | } 51 | endwin(); 52 | return 0; 53 | } 54 | 55 | void move_msg(int signum) { 56 | signal(SIGALRM, move_msg); 57 | move(row, col); 58 | addstr(BLANK); 59 | col += dir; 60 | move(row, col); 61 | addstr(MESSAGE); 62 | refresh(); 63 | 64 | // now handle borders 65 | if (dir == -1 && col <= 0) 66 | dir = 1; 67 | else if (dir == 1 && col + strlen(MESSAGE) >= COLS) 68 | dir = -1; 69 | } 70 | -------------------------------------------------------------------------------- /chapter7/bounce2d.c: -------------------------------------------------------------------------------- 1 | /** 2 | * bounce a character (default is 'o') around the screen 3 | * defined by some parameters 4 | * 5 | * user input: s slow down x component, S: slow y component 6 | * f speed up x component, F: speed y component 7 | * Q quit 8 | * 9 | * blocks on read, but timer tick sends SIGALRM caught by ball_move 10 | * build: gcc bounce2d.c set_ticker.c -lcurses -o bounce2d 11 | */ 12 | 13 | #include 14 | #include 15 | #include "bounce.h" 16 | #include "set_ticker.c" 17 | 18 | struct ppball the_ball; 19 | 20 | void set_up(); 21 | void wrap_up(); 22 | int bounce_or_lose(struct ppball *); 23 | 24 | int main() { 25 | int c; 26 | 27 | set_up(); 28 | 29 | while ((c = getchar()) != 'Q') { 30 | if (c == 'f') the_ball.x_ttm--; 31 | else if (c == 's') the_ball.x_ttm++; 32 | else if (c == 'F') the_ball.y_ttm--; 33 | else if (c == 'S') the_ball.y_ttm++; 34 | } 35 | wrap_up(); 36 | } 37 | 38 | // init structure and other stuff 39 | void set_up() { 40 | void ball_move(int); 41 | 42 | the_ball.y_pos = Y_INIT; 43 | the_ball.x_pos = X_INIT; 44 | the_ball.y_ttg = the_ball.y_ttm = Y_TTM; 45 | the_ball.x_ttg = the_ball.x_ttm = X_TTM; 46 | the_ball.y_dir = the_ball.x_dir = 1; 47 | the_ball.symbol = DFL_SYMBOL; 48 | 49 | initscr(); 50 | noecho(); 51 | crmode(); 52 | 53 | signal(SIGINT, SIG_IGN); 54 | mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 55 | refresh(); 56 | 57 | signal(SIGALRM, ball_move); 58 | set_ticker(1000 / TICKS_PER_SEC); 59 | } 60 | 61 | void wrap_up() { 62 | set_ticker(0); 63 | endwin(); 64 | } 65 | 66 | void ball_move(int signum) { 67 | int y_cur, x_cur, moved; 68 | 69 | signal(SIGALRM, SIG_IGN); 70 | y_cur = the_ball.y_pos; 71 | x_cur = the_ball.x_pos; 72 | moved = 0; 73 | 74 | if (the_ball.y_ttm > 0 & the_ball.y_ttg-- == 1) { 75 | the_ball.y_pos += the_ball.y_dir; 76 | the_ball.y_ttg = the_ball.y_ttm; 77 | moved = 1; 78 | } 79 | 80 | if (the_ball.x_ttm > 0 && the_ball.x_ttg-- == 1) { 81 | the_ball.x_pos += the_ball.x_dir; 82 | the_ball.x_ttg = the_ball.x_ttm; 83 | moved = 1; 84 | } 85 | 86 | if (moved) { 87 | mvaddch(y_cur, x_cur, BLANK); 88 | mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 89 | bounce_or_lose(&the_ball); 90 | move(LINES - 1, COLS - 1); 91 | refresh(); 92 | } 93 | signal(SIGALRM, ball_move); 94 | } 95 | 96 | int bounce_or_lose(struct ppball *bp) { 97 | int return_val = 0; 98 | if (bp->y_pos == TOP_ROW) { 99 | bp->y_dir = 1; 100 | return_val = 1; 101 | } else if (bp->y_pos == BOT_ROW) { 102 | bp->y_dir = -1; 103 | return_val = 1; 104 | } 105 | 106 | if (bp->x_pos == LEFT_EDGE) { 107 | bp->x_dir = 1; 108 | return_val = 1; 109 | } else if (bp->x_pos == RIGHT_EDGE) { 110 | bp->x_dir = -1; 111 | return_val = 1; 112 | } 113 | 114 | return return_val; 115 | } 116 | -------------------------------------------------------------------------------- /chapter7/curses_basic/hello1.c: -------------------------------------------------------------------------------- 1 | /* hello1.c 2 | * purpose show the minimal calls needed to use curses 3 | * outline initialize, draw stuff, wait for input, quit 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | int main() { 10 | initscr(); 11 | 12 | clear(); 13 | move(10, 20); 14 | addstr("Hello world"); 15 | move(LINES - 1, 0); 16 | refresh(); 17 | getch(); 18 | endwin(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /chapter7/curses_basic/hello2.c: -------------------------------------------------------------------------------- 1 | /* hello2.c 2 | * purpose show how to use curses functions with a loop 3 | * outline initialize, draw stuff, wrap up 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | int main() { 10 | int i; 11 | initscr(); 12 | clear(); 13 | 14 | for (i = 0; i < LINES; ++i) { 15 | move(i, i+1); 16 | if (i % 2 == 1) 17 | standout(); 18 | addstr("Hello world"); 19 | if (i % 2 == 1) 20 | standend(); 21 | } 22 | 23 | refresh(); 24 | getch(); 25 | endwin(); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /chapter7/curses_basic/hello3.c: -------------------------------------------------------------------------------- 1 | /* hello2.c 2 | * purpose show how to use curses functions with a loop 3 | * outline initialize, draw stuff, wrap up 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() { 11 | int i; 12 | initscr(); 13 | clear(); 14 | 15 | for (i = 0; i < LINES; ++i) { 16 | move(i, i+1); 17 | if (i % 2 == 1) 18 | standout(); 19 | addstr("Hello world"); 20 | if (i % 2 == 1) 21 | standend(); 22 | sleep(1); 23 | refresh(); 24 | } 25 | 26 | endwin(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /chapter7/curses_basic/hello4.c: -------------------------------------------------------------------------------- 1 | /* hello2.c 2 | * purpose show how to use curses functions with a loop 3 | * outline initialize, draw stuff, wrap up 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() { 11 | int i; 12 | initscr(); 13 | clear(); 14 | 15 | for (i = 0; i < LINES; ++i) { 16 | move(i, i+1); 17 | if (i % 2 == 1) 18 | standout(); 19 | addstr("Hello world"); 20 | if (i % 2 == 1) 21 | standend(); 22 | refresh(); 23 | sleep(1); 24 | move(i, i+1); 25 | addstr(" "); // erase line 26 | } 27 | 28 | endwin(); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /chapter7/curses_basic/hello5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * purpose show how to use curses functions with a loop 3 | * outline initialize, draw stuff, wrap up 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define LEFTEDGE 10 11 | #define RIGHTEDGE 30 12 | #define ROW 10 13 | 14 | int main() { 15 | char *message = "Hello"; 16 | char *blank = " "; 17 | int dir = 1; 18 | int pos = LEFTEDGE; 19 | 20 | initscr(); 21 | clear(); 22 | 23 | while (1) { 24 | move(ROW, pos); 25 | addstr(message); 26 | move(LINES-1, COLS-1); 27 | refresh(); 28 | sleep(1); 29 | move(ROW, pos); 30 | addstr(blank); 31 | pos += dir; 32 | if (pos >= RIGHTEDGE) 33 | dir = -1; 34 | if (pos <= LEFTEDGE) 35 | dir = 1; 36 | } 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /chapter7/set_ticker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /** 4 | * arranges for interval timer to issue SIGALRMs at regular intervals 5 | * returns -1 on error, 0 for ok 6 | * arg in milliseconds, converted into whole seconds and microseconds 7 | * note: set_ticker(0) turns off ticker 8 | */ 9 | int set_ticker(int n_msecs) { 10 | struct itimerval new_timeset; 11 | long n_sec, n_usecs; 12 | 13 | n_sec = n_msecs / 1000; 14 | n_usecs = (n_msecs % 1000) * 1000L; 15 | 16 | new_timeset.it_interval.tv_sec = n_sec; 17 | new_timeset.it_interval.tv_usec = n_usecs; 18 | 19 | new_timeset.it_value.tv_sec = n_sec; 20 | new_timeset.it_value.tv_usec = n_usecs; 21 | 22 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 23 | } 24 | -------------------------------------------------------------------------------- /chapter7/sigactdemo.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose: show use of sigaction() 3 | * feature: blocks ^\ while handling ^C 4 | * does not reset ^C handler, so two kill 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #define INPUTLEN 100 11 | 12 | int main() { 13 | struct sigaction new_hander; 14 | sigset_t blocked; 15 | void intHandler(); 16 | char x[INPUTLEN]; 17 | 18 | // load these two members first 19 | new_hander.sa_handler = intHandler; 20 | new_hander.sa_flags = SA_RESETHAND | SA_RESTART; 21 | 22 | // then build the list of blocked signals 23 | sigemptyset(&blocked); 24 | sigaddset(&blocked, SIGQUIT); 25 | new_hander.sa_mask = blocked; // store blockmask 26 | 27 | if (sigaction(SIGINT, &new_hander, NULL) == -1) 28 | perror("sigaction"); 29 | else 30 | while (1) { 31 | fgets(x, INPUTLEN, stdin); 32 | printf("input: %s", x); 33 | } 34 | } 35 | 36 | void intHandler(int s) { 37 | printf("Called with signal %d\n", s); 38 | sleep(s); 39 | printf("Done handling signal %d\n", s); 40 | } 41 | -------------------------------------------------------------------------------- /chapter7/sigdemo3.c: -------------------------------------------------------------------------------- 1 | /** 2 | * purpose : show answers to signal questions 3 | * questions1: does the handler stay in effect after a signal arrives? 4 | * questions2: what if a signalX arrives while handling signalX? 5 | * questions3: what if a signalY arrives while handling signalY? 6 | * questions4: what happens to read() when a signal arrives? 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define INPUTLEN 100 15 | 16 | int main(int argc, char *argv[]) { 17 | void intHandler(int); 18 | void quitHandler(int); 19 | char input[INPUTLEN]; 20 | int nchars; 21 | 22 | signal(SIGINT, intHandler); 23 | signal(SIGQUIT, quitHandler); 24 | 25 | do { 26 | printf("\nType a message\n"); 27 | nchars = read(0, input, (INPUTLEN - 1)); 28 | if (nchars == -1) { 29 | perror("read returned a error"); 30 | } else { 31 | input[nchars] = '\0'; 32 | printf("You typed: %s", input); 33 | } 34 | } while (strncmp(input, "quit", 4) != 0); 35 | 36 | } 37 | 38 | void intHandler(int s) { 39 | printf(" Received signal %d .. waiting\n", s); 40 | sleep(2); 41 | printf("Leaving intHandler\n"); 42 | } 43 | 44 | void quitHandler(int s) { 45 | printf(" Received signal %d .. waiting\n", s); 46 | sleep(3); 47 | printf("Leaving quitHandler\n"); 48 | } 49 | -------------------------------------------------------------------------------- /chapter7/ticker_demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demonstrates use of interval timer to generate reqular 3 | * signals, which are in turn caught and used to count down 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main() { 13 | void countdown(); 14 | int set_ticker(int); 15 | signal(SIGALRM, countdown); 16 | if (set_ticker(500) == -1) { 17 | perror("set_ticker"); 18 | } else { 19 | while (1) { 20 | pause(); 21 | } 22 | } 23 | return 0; 24 | } 25 | 26 | void countdown() { 27 | static int num = 10; 28 | printf("%d..", num--); 29 | fflush(stdout); 30 | if (num < 0) { 31 | printf("DONE!\n"); 32 | exit(0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter9/changeenv.c: -------------------------------------------------------------------------------- 1 | /** 2 | * show how to change the environment 3 | * note: call "env" to display its new settings 4 | */ 5 | #include 6 | #include 7 | 8 | extern char ** environ; 9 | 10 | int main() { 11 | char *table[3]; 12 | 13 | table[0] = "TERM=vt100"; 14 | table[1] = "HOME=/on/the/range"; 15 | table[2] = NULL; 16 | 17 | environ = table; 18 | execlp("env", "env", NULL); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /chapter9/env.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | int main() { 5 | char *cp = getenv("LANG"); 6 | if (cp) 7 | printf("%s\n", cp); 8 | else 9 | printf("he"); 10 | } 11 | -------------------------------------------------------------------------------- /chapter9/execute.c: -------------------------------------------------------------------------------- 1 | /** 2 | * code used by shell to execute commands 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * purpose: run a program passing it arguments 13 | * returns: status returned via wait, or -1 on error 14 | * errors: -1 on fork() or wait() errors 15 | */ 16 | int execute(char *argv[]) { 17 | int pid; 18 | int child_info = -1; 19 | 20 | if (argv[0] == NULL) 21 | return 0; 22 | if ((pid = fork()) == -1) { 23 | perror("fork"); 24 | } else if (pid == 0) { 25 | signal(SIGINT, SIG_DFL); 26 | signal(SIGQUIT, SIG_DFL); 27 | execvp(argv[0], argv); 28 | perror("cannot execute command"); 29 | exit(1); 30 | } else { 31 | if (wait(&child_info) == -1) { 32 | perror("wait"); 33 | } 34 | } 35 | return child_info; 36 | } 37 | -------------------------------------------------------------------------------- /chapter9/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc smsh1.c splitline.c execute.c -o smsh1 3 | clean: 4 | rm smsh1 5 | -------------------------------------------------------------------------------- /chapter9/smsh.h: -------------------------------------------------------------------------------- 1 | #include 2 | #define YES 1 3 | #define NO 0 4 | 5 | char *next_cmd(); 6 | char **splitline(char *); 7 | void freelist(char **); 8 | void *emalloc(size_t); 9 | void *erealloc(void *, size_t); 10 | int execute(char **); 11 | void fatal(char *, char *, int); 12 | -------------------------------------------------------------------------------- /chapter9/smsh1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * first really useful version after prompting shell 3 | * this one parses the command line into strings 4 | * uses fork, exec, wait, and ignores signals 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "smsh.h" 12 | 13 | #define DFL_PROMPT "> " 14 | 15 | int main() { 16 | char *cmdline, *prompt, **arglist; 17 | int result; 18 | void setup(); 19 | 20 | prompt = DFL_PROMPT; 21 | setup(); 22 | 23 | while ((cmdline = next_cmd(prompt, stdin)) != NULL) { 24 | if ((arglist = splitline(cmdline)) != NULL) { 25 | result = execute(arglist); 26 | freelist(arglist); 27 | } 28 | free(cmdline); 29 | } 30 | return 0; 31 | } 32 | 33 | /** 34 | * purpose: initialize shell 35 | * returns: nothing. calls fatal() if trouble 36 | */ 37 | void setup() { 38 | signal(SIGINT, SIG_IGN); 39 | signal(SIGQUIT, SIG_IGN); 40 | } 41 | 42 | void fatal(char *s1, char *s2, int n) { 43 | fprintf(stderr, "Error: %s, %s\n", s1, s2); 44 | exit(n); 45 | } 46 | -------------------------------------------------------------------------------- /chapter9/splitline.c: -------------------------------------------------------------------------------- 1 | /** 2 | * command reading and parsing functions for smsh 3 | * char *next_cmd() - get next command 4 | * char **splitline(char *) - parse a string 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include "smsh.h" 10 | 11 | /** 12 | * purpose: read next command line from fp 13 | * returns: dynamically allocated string holding command line 14 | * errors: NULL at EOF 15 | * calls fatal() 16 | * notes: allocates space in BUFSIZ chunks 17 | */ 18 | 19 | char * next_cmd(char * prompt, FILE *fp) { 20 | char *buf; 21 | int bufspace = 0; 22 | int pos = 0; 23 | int c; 24 | 25 | printf("%s", prompt); 26 | while ((c = getc(fp)) != EOF) { 27 | // need space ? 28 | if (pos + 1 >= bufspace) { 29 | if (bufspace == 0) 30 | buf = emalloc(BUFSIZ); 31 | else 32 | buf = erealloc(buf, bufspace + BUFSIZ); 33 | bufspace += BUFSIZ; 34 | } 35 | 36 | // end of command? 37 | if (c == '\n') 38 | break; 39 | 40 | // no, add to buffer 41 | buf[pos++] = c; 42 | } 43 | if (c == EOF && pos == 0) 44 | return NULL; 45 | buf[pos] = '\0'; 46 | return buf; 47 | } 48 | 49 | #define is_delim(x) ((x) == ' ' || (x) == '\t') 50 | 51 | /** 52 | * purpose: split a line into array of white-space separated tokens 53 | * returns: a NULL-terminated array of pointers to copies of the 54 | * tokens or NULL if line if no tokens on the line 55 | * action: traverse the array, locate strings, make copies 56 | * note: strtok() could work, but we may want to add quotes later 57 | */ 58 | 59 | char ** splitline(char *line) { 60 | char *newstr(); 61 | char **args; 62 | int spots = 0; 63 | int bufspace = 0; 64 | int argnum = 0; 65 | char *cp = line; 66 | char *start; 67 | int len; 68 | 69 | if (line == NULL) 70 | return NULL; 71 | 72 | args = emalloc(BUFSIZ); 73 | bufspace = BUFSIZ; 74 | spots = BUFSIZ / sizeof(char *); 75 | 76 | while (*cp != '\0') { 77 | while (is_delim(*cp)) 78 | cp++; 79 | if (*cp == '\0') 80 | break; 81 | 82 | // make sure the array has romm (+ 1 for NULL) 83 | if (argnum + 1 >= spots) { 84 | args = erealloc(args, bufspace + BUFSIZ); 85 | bufspace += BUFSIZ; 86 | spots += (BUFSIZ / sizeof(char *)); 87 | } 88 | 89 | // mark start, then find end of word 90 | start = cp; 91 | len = 1; 92 | while (*++cp != '\0' && !(is_delim(*cp))) 93 | len++; 94 | 95 | args[argnum++] = newstr(start, len); 96 | } 97 | args[argnum] = NULL; 98 | return args; 99 | } 100 | 101 | /** 102 | * purpose: constructor for strings 103 | * returns: a string, never NULL 104 | */ 105 | char *newstr(char *s, int len) { 106 | char *rv = emalloc(len + 1); 107 | 108 | rv[len] = '\0'; 109 | strncpy(rv, s, len); 110 | return rv; 111 | } 112 | 113 | /** 114 | * purpose: free the list returned by splitline 115 | * returns: nothing 116 | * action: free all strings in list and then free the list 117 | */ 118 | void freelist(char **list) { 119 | char **cp = list; 120 | while (*cp) 121 | free(*cp++); 122 | free(list); 123 | } 124 | 125 | void *emalloc(size_t n) { 126 | void *rv; 127 | if ((rv = malloc(n)) == NULL) 128 | fatal("out of memory", "", 1); 129 | return rv; 130 | } 131 | 132 | void *erealloc(void *p, size_t n) { 133 | void *rv; 134 | if ((rv = realloc(p, n)) == NULL) 135 | fatal("realloc() failed", "", 1); 136 | return rv; 137 | } 138 | --------------------------------------------------------------------------------