├── gdb ├── .gitignore ├── Makefile └── head.c ├── make ├── .cvsignore ├── http.c ├── network.c ├── main.c └── Makefile ├── link ├── .gitignore ├── calc.c ├── Makefile └── dlcalc.c ├── shlib ├── .gitignore ├── a.c ├── b.c ├── myprog.c └── Makefile ├── .gitattributes ├── [연습문제답안]모두를위한리눅스프로그래밍.pdf ├── hello.c ├── bell.c ├── segv.c ├── env.c ├── logging.c ├── echo.c ├── args.c ├── isatty.c ├── showenv.c ├── sig.c ├── feof-bug.c ├── pwd.c ├── pwd3.c ├── array.c ├── ln.c ├── mv.c ├── getcperf.c ├── symlink.c ├── rm.c ├── rmdir.c ├── chmod.c ├── mkdir.c ├── user.c ├── cat2.c ├── cat0.c ├── touch.c ├── head.c ├── trap.c ├── sigqueue-test.c ├── ls.c ├── pwd2.c ├── chown.c ├── dupread.c ├── .gitignore ├── chgrp.c ├── timefmt.c ├── cat3.c ├── wc-l-stdio.c ├── cat5.c ├── data.esc0 ├── mapwrite.c ├── cat.c ├── data.esc ├── head2.c ├── spawn.c ├── LICENSES ├── Makefile ├── tail.c ├── cat-escape.c ├── grep.c ├── head3.c ├── wc-l-syscall.c ├── id.c ├── daytime.c ├── stat.c ├── catdir.c ├── wgrep.c ├── strftime.c ├── slice.c ├── head4.c ├── echoclient.c ├── cat4.c ├── exec.c ├── tail2.c ├── ls2.c ├── grep3.c ├── daytimed.c ├── grep2.c ├── mkpath.c ├── slice2.c ├── sh1.c ├── traverse.c ├── README.md ├── sh2.c ├── test-scripts.sh ├── httpd.c └── httpd2.c /gdb/.gitignore: -------------------------------------------------------------------------------- 1 | head 2 | -------------------------------------------------------------------------------- /make/.cvsignore: -------------------------------------------------------------------------------- 1 | httpd 2 | -------------------------------------------------------------------------------- /link/.gitignore: -------------------------------------------------------------------------------- 1 | calc 2 | dlcalc 3 | -------------------------------------------------------------------------------- /make/http.c: -------------------------------------------------------------------------------- 1 | int 2 | b(void) 3 | { 4 | return 1; 5 | } 6 | -------------------------------------------------------------------------------- /make/network.c: -------------------------------------------------------------------------------- 1 | int 2 | a(void) 3 | { 4 | return 1; 5 | } 6 | -------------------------------------------------------------------------------- /shlib/.gitignore: -------------------------------------------------------------------------------- 1 | myprog 2 | libmy.so.1.0 3 | libmy.so.1 4 | libmy.so 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /[연습문제답안]모두를위한리눅스프로그래밍.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jpub/Linux_for_Everyone/HEAD/[연습문제답안]모두를위한리눅스프로그래밍.pdf -------------------------------------------------------------------------------- /gdb/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g -O0 -Wall 3 | 4 | head: head.c 5 | $(CC) $(CFLAGS) -o $@ head.c 6 | -------------------------------------------------------------------------------- /hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(int argc, char *argv[]) 5 | { 6 | printf("Hello, World!\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /bell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | printf("\007"); 8 | exit(0); 9 | } 10 | -------------------------------------------------------------------------------- /shlib/a.c: -------------------------------------------------------------------------------- 1 | static int a(int n); 2 | 3 | int 4 | sample_function1(int n) 5 | { 6 | return a(n); 7 | } 8 | 9 | static int 10 | a(int n) 11 | { 12 | return n * 2; 13 | } 14 | -------------------------------------------------------------------------------- /shlib/b.c: -------------------------------------------------------------------------------- 1 | static int a(int n); 2 | 3 | int 4 | sample_function2(int n) 5 | { 6 | return a(n); 7 | } 8 | 9 | static int 10 | a(int n) 11 | { 12 | return n * n; 13 | } 14 | -------------------------------------------------------------------------------- /segv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | int *p = NULL; 8 | 9 | printf("%d\n", *p); 10 | exit(0); 11 | } 12 | -------------------------------------------------------------------------------- /link/calc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | printf("sin(0.5)=%g\n", sin(0.5)); 9 | exit(0); 10 | } 11 | -------------------------------------------------------------------------------- /make/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern int a(void); 5 | extern int b(void); 6 | 7 | int 8 | main(int argc, char **argv) 9 | { 10 | a(); 11 | b(); 12 | exit(0); 13 | } 14 | -------------------------------------------------------------------------------- /link/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS = calc dlcalc 2 | 3 | all: $(TARGETS) 4 | 5 | calc: calc.c 6 | gcc -g -Wall calc.c -lm -o $@ 7 | 8 | dlcalc: dlcalc.c 9 | gcc -g -Wall dlcalc.c -ldl -lm -o $@ 10 | 11 | clean: 12 | rm -f $(TARGETS) 13 | -------------------------------------------------------------------------------- /env.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern char **environ; 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | char **p; 10 | 11 | for (p = environ; *p; p++) { 12 | printf("%s\n", *p); 13 | } 14 | exit(0); 15 | } 16 | -------------------------------------------------------------------------------- /logging.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | openlog("xxxx", LOG_PID, LOG_DAEMON); 10 | syslog(LOG_NOTICE, "test message"); 11 | exit(0); 12 | } 13 | -------------------------------------------------------------------------------- /echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | int i; 8 | 9 | for (i = 1; i < argc; i++) { 10 | printf("%s%s", (i==1?"":" "), argv[i]); 11 | } 12 | putchar('\n'); 13 | exit(0); 14 | } 15 | -------------------------------------------------------------------------------- /args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | int i; 8 | 9 | printf("argc=%d\n", argc); 10 | for (i = 0; i < argc; i++) { 11 | printf("argv[%d]=%s\n", i, argv[i]); 12 | } 13 | exit(0); 14 | } 15 | -------------------------------------------------------------------------------- /isatty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | printf("stdin: %d\n", isatty(0)); 9 | printf("stdout: %d\n", isatty(1)); 10 | printf("stderr: %d\n", isatty(2)); 11 | exit(0); 12 | } 13 | -------------------------------------------------------------------------------- /showenv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | int i; 8 | 9 | for (i = 1; i < argc; i++) { 10 | char *val = getenv(argv[i]); 11 | if (val) printf("%s\n", val); 12 | } 13 | exit(0); 14 | } 15 | -------------------------------------------------------------------------------- /sig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void 7 | showsig(int sig) 8 | { 9 | printf("signal=%d\n", sig); 10 | } 11 | 12 | int 13 | main(int argc, char *argv[]) 14 | { 15 | signal(SIGINT, showsig); 16 | pause(); 17 | exit(0); 18 | } 19 | -------------------------------------------------------------------------------- /feof-bug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLINE 16 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | FILE *f = stdin; 10 | char buf[MAXLINE + 1]; 11 | 12 | while (!feof(f)) { 13 | fgets(buf, MAXLINE, f); 14 | fputs(buf, stdout); 15 | } 16 | exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /pwd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | char buf[PATH_MAX]; 10 | 11 | if (!getcwd(buf, PATH_MAX)) { 12 | perror("getcwd"); 13 | exit(1); 14 | } 15 | puts(buf); 16 | exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /pwd3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | char *path; 9 | 10 | path = getcwd(NULL, 0); 11 | if (!path) { 12 | perror(path); 13 | exit(1); 14 | } 15 | puts(path); 16 | free(path); 17 | exit(0); 18 | } 19 | -------------------------------------------------------------------------------- /array.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | char *str1 = "Hello, World!\n"; 8 | char buf[14] = { 72, 101, 108, 108, 111, 44, 32, 9 | 87, 111, 114, 108, 100, 33, 10 }; 10 | char *str2 = buf; 11 | 12 | printf("%s", str1); 13 | printf("%s", str2); 14 | exit(0); 15 | } 16 | -------------------------------------------------------------------------------- /ln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | if (argc != 3) { 9 | fprintf(stderr, "%s: wrong arguments\n", argv[0]); 10 | exit(1); 11 | } 12 | if (link(argv[1], argv[2]) < 0) { 13 | perror(argv[1]); 14 | exit(1); 15 | } 16 | exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /make/Makefile: -------------------------------------------------------------------------------- 1 | #@range_begin(rules) 2 | CC = gcc 3 | CFLAGS = -g -Wall 4 | OBJS = main.o network.o http.o 5 | 6 | httpd: $(OBJS) 7 | $(CC) $(CFLAGS) -o $@ $(OBJS) 8 | 9 | main.o: main.c 10 | $(CC) -c $(CFLAGS) -o $@ $< 11 | 12 | network.o: network.c 13 | $(CC) -c $(CFLAGS) -o $@ $< 14 | 15 | http.o: http.c 16 | $(CC) -c $(CFLAGS) -o $@ $< 17 | #@range_end(rules) 18 | -------------------------------------------------------------------------------- /mv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | if (argc != 3) { 9 | fprintf(stderr, "%s: wrong arguments\n", argv[0]); 10 | exit(1); 11 | } 12 | if (rename(argv[1], argv[2]) < 0) { 13 | perror(argv[1]); 14 | exit(1); 15 | } 16 | exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /getcperf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int c; 9 | 10 | if ((argc > 2) && (argv[1][0] == 'f')) { 11 | while ((c = fgetc(stdin)) != EOF) 12 | ; 13 | } 14 | else { 15 | while ((c = getc(stdin)) != EOF) 16 | ; 17 | } 18 | exit(0); 19 | } 20 | -------------------------------------------------------------------------------- /symlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | if (argc != 3) { 9 | fprintf(stderr, "%s: wrong number of arguments\n", argv[0]); 10 | exit(1); 11 | } 12 | if (symlink(argv[1], argv[2]) < 0) { 13 | perror(argv[1]); 14 | exit(1); 15 | } 16 | exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /rm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | if (argc < 2) { 11 | fprintf(stderr, "%s: no arguments\n", argv[0]); 12 | exit(1); 13 | } 14 | for (i = 1; i < argc; i++) { 15 | if (unlink(argv[i]) < 0) { 16 | perror(argv[i]); 17 | exit(1); 18 | } 19 | } 20 | exit(0); 21 | } 22 | -------------------------------------------------------------------------------- /rmdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | if (argc < 2) { 11 | fprintf(stderr, "%s: no arguments\n", argv[0]); 12 | exit(1); 13 | } 14 | for (i = 1; i < argc; i++) { 15 | if (rmdir(argv[i]) < 0) { 16 | perror(argv[i]); 17 | exit(1); 18 | } 19 | } 20 | exit(0); 21 | } 22 | -------------------------------------------------------------------------------- /shlib/myprog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern int sample_function1(int n); 6 | extern int sample_function2(int n); 7 | 8 | int 9 | main(int argc, char *argv[]) 10 | { 11 | if (argc < 2) { 12 | fprintf(stderr, "no arg\n"); 13 | exit(1); 14 | } 15 | printf("n * 2 = %d\n", sample_function1(atoi(argv[1]))); 16 | printf("n ** 2 = %d\n", sample_function2(atoi(argv[1]))); 17 | exit(0); 18 | } 19 | -------------------------------------------------------------------------------- /chmod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int mode; 9 | int i; 10 | 11 | if (argc < 2) { 12 | fprintf(stderr, "no mode given\n"); 13 | exit(1); 14 | } 15 | mode = strtol(argv[1], NULL, 8); 16 | for (i = 2; i < argc; i++) { 17 | if (chmod(argv[i], mode) < 0) { 18 | perror(argv[i]); 19 | } 20 | } 21 | exit(0); 22 | } 23 | -------------------------------------------------------------------------------- /mkdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | int i; 10 | 11 | if (argc < 2) { 12 | fprintf(stderr, "%s: no arguments\n", argv[0]); 13 | exit(1); 14 | } 15 | for (i = 1; i < argc; i++) { 16 | if (mkdir(argv[i], 0777) < 0) { 17 | perror(argv[i]); 18 | exit(1); 19 | } 20 | } 21 | exit(0); 22 | } 23 | -------------------------------------------------------------------------------- /user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | struct passwd *pw; 11 | 12 | if (argc < 2) { 13 | fprintf(stderr, "no argument\n"); 14 | exit(1); 15 | } 16 | pw = getpwnam(argv[1]); 17 | if (!pw) { 18 | perror(argv[1]); 19 | exit(1); 20 | } 21 | printf("id=%d\n", pw->pw_uid); 22 | exit(0); 23 | } 24 | -------------------------------------------------------------------------------- /cat2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, char *argv[]) 6 | { 7 | int i; 8 | 9 | for (i = 1; i < argc; i++) { 10 | FILE *f; 11 | int c; 12 | 13 | f = fopen(argv[i], "r"); 14 | if (!f) { 15 | perror(argv[i]); 16 | exit(1); 17 | } 18 | while ((c = fgetc(f)) != EOF) { 19 | if (putchar(c) < 0) exit(1); 20 | } 21 | fclose(f); 22 | } 23 | exit(0); 24 | } 25 | -------------------------------------------------------------------------------- /cat0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | main(int argc, char *argv[]) 10 | { 11 | int fd; 12 | unsigned char buf[BUFSIZ]; 13 | int n; 14 | 15 | fd = open(argv[1], O_RDONLY); 16 | for (;;) { 17 | n = read(fd, buf, BUFSIZ); 18 | if (n == 0) break; 19 | n = write(STDOUT_FILENO, buf, n); 20 | } 21 | close(fd); 22 | exit(0); 23 | } 24 | -------------------------------------------------------------------------------- /shlib/Makefile: -------------------------------------------------------------------------------- 1 | # Simple example of shared library. 2 | 3 | all: exec 4 | 5 | exec: myprog libmy.so.1 6 | LD_LIBRARY_PATH=`pwd` ./myprog 7 7 | 8 | .SUFFIXES: 9 | .SUFFIXES: .c .o 10 | .c.o: 11 | gcc -g -Wall -fPIC -c $< -o $@ 12 | 13 | myprog: myprog.o libmy.so 14 | gcc -L. myprog.o -lmy -o $@ 15 | 16 | libmy.so.1.0: a.o b.o 17 | gcc -shared -Wl,-soname,libmy.so.1 a.o b.o -lc -o $@ 18 | 19 | libmy.so.1: libmy.so.1.0 20 | ln -sf $< $@ 21 | 22 | libmy.so: libmy.so.1.0 23 | ln -sf $< $@ 24 | 25 | clean: 26 | rm -f myprog libmy* *.o 27 | -------------------------------------------------------------------------------- /touch.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple touch(1) command. 3 | This program does not create file, just update atime/mtime. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | int i; 15 | 16 | for (i = 0; i < argc; i++) { 17 | if (utime(argv[i], NULL) < 0) { 18 | perror(argv[i]); 19 | /* We do not exit here... 20 | * exit(1); 21 | */ 22 | } 23 | } 24 | exit(0); 25 | } 26 | -------------------------------------------------------------------------------- /link/dlcalc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef double (*mathfunc_t)(double); 7 | 8 | int 9 | main(int argc, char *argv[]) 10 | { 11 | void *libm; 12 | mathfunc_t sin; 13 | double result; 14 | 15 | libm = dlopen("libm.so", RTLD_NOW); 16 | if (!libm) { 17 | fprintf(stderr, "dlopen failed"); 18 | exit(1); 19 | } 20 | sin = (mathfunc_t)dlsym(libm, "sin"); 21 | result = sin(0.5); 22 | printf("sin(0.5)=%g\n", result); 23 | exit(0); 24 | } 25 | -------------------------------------------------------------------------------- /head.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void do_head(FILE *f, long nlines); 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | if (argc != 2) { 10 | fprintf(stderr, "Usage: %s n\n", argv[0]); 11 | exit(1); 12 | } 13 | do_head(stdin, atol(argv[1])); 14 | exit(0); 15 | } 16 | 17 | static void 18 | do_head(FILE *f, long nlines) 19 | { 20 | int c; 21 | 22 | if (nlines <= 0) return; 23 | while ((c = getc(f)) != EOF) { 24 | if (putchar(c) < 0) exit(1); 25 | if (c == '\n') { 26 | nlines--; 27 | if (nlines == 0) return; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef void (*sighandler_t)(int); 7 | 8 | sighandler_t 9 | trap_signal(int sig, sighandler_t handler) 10 | { 11 | struct sigaction act, old; 12 | 13 | act.sa_handler = handler; 14 | sigemptyset(&act.sa_mask); 15 | act.sa_flags = SA_RESTART; 16 | if (sigaction(sig, &act, &old) < 0) 17 | return NULL; 18 | 19 | return old.sa_handler; 20 | } 21 | 22 | void 23 | print_exit(int sig) 24 | { 25 | printf("Got signal %d\n", sig); 26 | exit(0); 27 | } 28 | 29 | int 30 | main(int argc, char *argv[]) 31 | { 32 | trap_signal(SIGINT, print_exit); 33 | pause(); 34 | exit(0); 35 | } 36 | -------------------------------------------------------------------------------- /sigqueue-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef void (*sighandler_t)(int); 7 | 8 | sighandler_t 9 | trap_signal(int sig, sighandler_t handler) 10 | { 11 | struct sigaction act, old; 12 | 13 | act.sa_handler = handler; 14 | sigemptyset(&act.sa_mask); 15 | act.sa_flags = SA_RESTART; 16 | if (sigaction(sig, &act, &old) < 0) 17 | return NULL; 18 | 19 | return old.sa_handler; 20 | } 21 | 22 | void 23 | msg_sleep(int sig) 24 | { 25 | puts("got SIGINT, sleep..."); 26 | sleep(3); 27 | } 28 | 29 | int 30 | main(int argc, char *argv[]) 31 | { 32 | trap_signal(SIGINT, msg_sleep); 33 | pause(); 34 | exit(0); 35 | } 36 | -------------------------------------------------------------------------------- /ls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void do_ls(char *path); 7 | 8 | int 9 | main(int argc, char *argv[]) 10 | { 11 | int i; 12 | 13 | if (argc < 2) { 14 | fprintf(stderr, "%s: no arguments\n", argv[0]); 15 | exit(1); 16 | } 17 | for (i = 1; i < argc; i++) { 18 | do_ls(argv[i]); 19 | } 20 | exit(0); 21 | } 22 | 23 | static void 24 | do_ls(char *path) 25 | { 26 | DIR *d; 27 | struct dirent *ent; 28 | 29 | d = opendir(path); /* (1) */ 30 | if (!d) { 31 | perror(path); 32 | exit(1); 33 | } 34 | while (ent = readdir(d)) { /* (2) */ 35 | printf("%s\n", ent->d_name); 36 | } 37 | closedir(d); /* (1') */ 38 | } 39 | -------------------------------------------------------------------------------- /pwd2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define INIT_BUFSIZE 1024 7 | 8 | char* 9 | my_getcwd(void) 10 | { 11 | char *buf, *tmp; 12 | size_t size = INIT_BUFSIZE; 13 | 14 | buf = malloc(size); 15 | if (!buf) return NULL; 16 | for (;;) { 17 | errno = 0; 18 | if (getcwd(buf, size)) 19 | return buf; 20 | if (errno != ERANGE) break; 21 | size *= 2; 22 | tmp = realloc(buf, size); 23 | if (!tmp) break; 24 | buf = tmp; 25 | } 26 | free(buf); 27 | return NULL; 28 | } 29 | 30 | int 31 | main(int argc, char *argv[]) 32 | { 33 | char *path; 34 | 35 | path = my_getcwd(); 36 | if (!path) { 37 | perror("getcwd"); 38 | exit(1); 39 | } 40 | puts(path); 41 | free(path); 42 | exit(0); 43 | } 44 | -------------------------------------------------------------------------------- /chown.c: -------------------------------------------------------------------------------- 1 | /* 2 | Usage: chown ... 3 | 4 | must be a user name. User ID is not supported. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static uid_t get_user_id(char *user); 13 | 14 | int 15 | main(int argc, char *argv[]) 16 | { 17 | uid_t uid; 18 | int i; 19 | 20 | if (argc < 2) { 21 | fprintf(stderr, "no user name given\n"); 22 | exit(1); 23 | } 24 | uid = get_user_id(argv[1]); 25 | for (i = 2; i < argc; i++) { 26 | if (chown(argv[i], uid, -1) < 0) { 27 | perror(argv[i]); 28 | } 29 | } 30 | exit(1); 31 | } 32 | 33 | static uid_t 34 | get_user_id(char *user) 35 | { 36 | struct passwd *pw; 37 | 38 | pw = getpwnam(user); 39 | if (!pw) { 40 | fprintf(stderr, "no such user: %s\n", user); 41 | exit(1); 42 | } 43 | return pw->pw_uid; 44 | } 45 | -------------------------------------------------------------------------------- /dupread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void 7 | readshow(int fd) 8 | { 9 | char buf[8]; 10 | int n; 11 | 12 | n = read(fd, buf, 1); 13 | if (n < 0) { 14 | perror("read"); 15 | exit(1); 16 | } 17 | if (n == 0) 18 | exit(0); 19 | switch (buf[0]) { 20 | case ' ': strcpy(buf, "' '"); break; 21 | case '\n': strcpy(buf, "\\n"); break; 22 | case '\r': strcpy(buf, "\\r"); break; 23 | case '\t': strcpy(buf, "\\t"); break; 24 | case '\v': strcpy(buf, "\\v"); break; 25 | default: 26 | buf[1] = '\0'; 27 | break; 28 | } 29 | printf("fd%d: %s\n", fd, buf); 30 | } 31 | 32 | int 33 | main(int argc, char *argv[]) 34 | { 35 | int newfd; 36 | 37 | newfd = dup(0); 38 | for (;;) { 39 | readshow(0); 40 | readshow(newfd); 41 | } 42 | exit(0); 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | core 2 | data 3 | 4 | align 5 | args 6 | array 7 | bell 8 | cat 9 | cat-escape 10 | cat0 11 | cat2 12 | cat3 13 | cat3 14 | cat4 15 | cat5 16 | catdir 17 | chgrp 18 | chmod 19 | chown 20 | daytime 21 | daytimed 22 | dupread 23 | echo 24 | echoclient 25 | env 26 | feof-bug 27 | ferror-test 28 | exec 29 | getcperf 30 | getctty 31 | grep 32 | grep2 33 | grep3 34 | head 35 | head2 36 | head3 37 | head4 38 | hello 39 | httpd 40 | httpd2 41 | id 42 | isatty 43 | ln 44 | logging 45 | ls 46 | ls2 47 | mapwrite 48 | mapwrite.data 49 | memmon 50 | mkdir 51 | mkpath 52 | mv 53 | namemax 54 | progname 55 | pwd 56 | pwd2 57 | pwd3 58 | rm 59 | rmdir 60 | segv 61 | sh1 62 | sh2 63 | show-vmmap 64 | showenv 65 | sig 66 | sigqueue-test 67 | sizeof 68 | sleep 69 | spawn 70 | stat 71 | strftime 72 | strto 73 | symlink 74 | tail 75 | tail2 76 | timefmt 77 | times 78 | touch 79 | trap 80 | traverse 81 | unsignedchar 82 | user 83 | wc-l-stdio 84 | wc-l-syscall 85 | wgrep 86 | slice 87 | slice2 88 | -------------------------------------------------------------------------------- /chgrp.c: -------------------------------------------------------------------------------- 1 | /* 2 | Usage: chgrp ... 3 | 4 | must be a group name. Group ID is not supported. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static gid_t get_group_id(char *group); 14 | 15 | int 16 | main(int argc, char *argv[]) 17 | { 18 | gid_t gid; 19 | int i; 20 | 21 | if (argc < 2) { 22 | fprintf(stderr, "no group name given\n"); 23 | exit(1); 24 | } 25 | gid = get_group_id(argv[1]); 26 | for (i = 2; i < argc; i++) { 27 | if (chown(argv[i], -1, gid) < 0) { 28 | perror(argv[i]); 29 | } 30 | } 31 | exit(1); 32 | } 33 | 34 | static gid_t 35 | get_group_id(char *group) 36 | { 37 | struct group *gr; 38 | 39 | gr = getgrnam(group); 40 | if (!gr) { 41 | fprintf(stderr, "no such group: %s\n", group); 42 | exit(1); 43 | } 44 | return gr->gr_gid; 45 | } 46 | -------------------------------------------------------------------------------- /timefmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | main(int argc, char *argv[]) 10 | { 11 | time_t t; 12 | struct tm *tm; 13 | struct timeval tv; 14 | 15 | setlocale(LC_TIME, ""); 16 | 17 | /* time(2), ctime(3), gettimeofday(2) */ 18 | time(&t); 19 | gettimeofday(&tv, NULL); 20 | printf("time = %ld\n", (long)t); 21 | printf("ctime = %s", ctime(&t)); 22 | printf("tv.tv_sec = %ld\n", (long)tv.tv_sec); 23 | printf("tv.tv_usec = %ld\n", (long)tv.tv_usec); 24 | printf("ctime(tv) = %s", ctime(&tv.tv_sec)); 25 | 26 | /* gmtime(3), localtime(3) */ 27 | tm = gmtime(&t); 28 | printf("asctime(UTC) = %s", asctime(tm)); 29 | printf("mktime(UTC) = %ld\n", (long)t); 30 | tm = localtime(&t); 31 | printf("asctime(LOC) = %s", asctime(tm)); 32 | printf("mktime(LOC) = %ld\n", (long)t); 33 | 34 | exit(0); 35 | } 36 | -------------------------------------------------------------------------------- /cat3.c: -------------------------------------------------------------------------------- 1 | /* 2 | cat3.c -- read stdin if no argument given. 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | static void do_cat(FILE *f); 15 | 16 | int 17 | main(int argc, char *argv[]) 18 | { 19 | int i; 20 | 21 | if (argc == 1) { 22 | do_cat(stdin); 23 | } 24 | else { 25 | for (i = 1; i < argc; i++) { 26 | FILE *f; 27 | 28 | f = fopen(argv[i], "r"); 29 | if (!f) { 30 | perror(argv[i]); 31 | exit(1); 32 | } 33 | do_cat(f); 34 | fclose(f); 35 | } 36 | } 37 | exit(0); 38 | } 39 | 40 | static void 41 | do_cat(FILE *f) 42 | { 43 | int c; 44 | 45 | while ((c = fgetc(f)) != EOF) { 46 | if (putchar(c) < 0) exit(1); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /wc-l-stdio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void do_wc_l(FILE *f); 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | if (argc == 1) { 10 | do_wc_l(stdin); 11 | } else { 12 | int i; 13 | 14 | for (i = 1; i < argc; i++) { 15 | FILE *f; 16 | 17 | f = fopen(argv[i], "r"); 18 | if (!f) { 19 | perror(argv[i]); 20 | exit(1); 21 | } 22 | do_wc_l(f); 23 | fclose(f); 24 | } 25 | } 26 | exit(0); 27 | } 28 | 29 | static void 30 | do_wc_l(FILE *f) 31 | { 32 | unsigned long n; 33 | int c; 34 | int prev = '\n'; /* '\n' is for empty file */ 35 | 36 | n = 0; 37 | while ((c = getc(f)) != EOF) { 38 | if (c == '\n') { 39 | n++; 40 | } 41 | prev = c; 42 | } 43 | if (prev != '\n') { 44 | /* missing '\n' at the end of file */ 45 | n++; 46 | } 47 | printf("%lu\n", n); 48 | } 49 | -------------------------------------------------------------------------------- /cat5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void do_cat(const char *path); 5 | static void die(const char *s); 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | if (argc < 2) { 11 | fprintf(stderr, "%s: file name not given\n", argv[0]); 12 | exit(1); 13 | } 14 | int i; 15 | for (i = 1; i < argc; i++) { 16 | do_cat(argv[i]); 17 | } 18 | exit(0); 19 | } 20 | 21 | #define BUFFER_SIZE 2048 22 | 23 | static void 24 | do_cat(const char *path) 25 | { 26 | unsigned char buf[BUFFER_SIZE]; 27 | FILE *f = fopen(path, "r"); 28 | if (!f) die(path); 29 | for (;;) { 30 | size_t n_read = fread(buf, 1, sizeof buf, f); 31 | if (ferror(f)) die(path); 32 | size_t n_written = fwrite(buf, 1, n_read, stdout); 33 | if (n_written < n_read) die(path); 34 | if (n_read < sizeof buf) break; 35 | } 36 | if (fclose(f) != 0) die(path); 37 | } 38 | 39 | static void 40 | die(const char *s) 41 | { 42 | perror(s); 43 | exit(1); 44 | } 45 | -------------------------------------------------------------------------------- /data.esc0: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void do_cat(const char *path); 9 | static void die(const char *s); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | int i; 15 | 16 | if (argc < 2) { 17 | fprintf(stderr, "%s: file name not given\n", argv[0]); 18 | exit(1); 19 | } 20 | for (i = 1; i < argc; i++) { 21 | do_cat(argv[i]); 22 | } 23 | exit(0); 24 | } 25 | 26 | #define BUFFER_SIZE 2048 27 | 28 | static void 29 | do_cat(const char *path) 30 | { 31 | int fd; 32 | unsigned char buf[BUFFER_SIZE]; 33 | int n; 34 | 35 | fd = open(path, O_RDONLY); 36 | if (fd < 0) die(path); 37 | for (;;) { 38 | n = read(fd, buf, sizeof buf); 39 | if (n < 0) die(path); 40 | if (n == 0) break; 41 | if (write(STDOUT_FILENO, buf, n) < 0) die(path); 42 | } 43 | if (close(fd) < 0) die(path); 44 | } 45 | 46 | static void 47 | die(const char *s) 48 | { 49 | perror(s); 50 | exit(1); 51 | } -------------------------------------------------------------------------------- /mapwrite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define DATAFILE "mapwrite.data" 8 | 9 | int 10 | main(int argc, char *argv[]) 11 | { 12 | int fd; 13 | char *ptr; 14 | int len = 3; 15 | 16 | /* prepare data file */ 17 | unlink(DATAFILE); /* ignore error */ 18 | fd = open(DATAFILE, O_WRONLY|O_CREAT, 0666); 19 | if (fd < 0) { 20 | perror(DATAFILE); 21 | exit(1); 22 | } 23 | write(fd, "NO\n", 3); 24 | close(fd); 25 | 26 | /* write by mmap */ 27 | fd = open(DATAFILE, O_RDWR); 28 | if (fd < 0) { 29 | perror(DATAFILE); 30 | exit(1); 31 | } 32 | ptr = (char*)mmap(0, len, PROT_WRITE, MAP_SHARED, fd, 0); 33 | if (!ptr) { 34 | perror("mmap(2)"); 35 | exit(1); 36 | } 37 | close(fd); 38 | ptr[0] = 'O'; 39 | ptr[1] = 'K'; 40 | ptr[2] = '\n'; 41 | munmap(ptr, len); 42 | 43 | /* cat it */ 44 | system("cat " DATAFILE); 45 | 46 | exit(0); 47 | } 48 | -------------------------------------------------------------------------------- /cat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void do_cat(const char *path); 9 | static void die(const char *s); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | int i; 15 | 16 | if (argc < 2) { 17 | fprintf(stderr, "%s: file name not given\n", argv[0]); 18 | exit(1); 19 | } 20 | for (i = 1; i < argc; i++) { 21 | do_cat(argv[i]); 22 | } 23 | exit(0); 24 | } 25 | 26 | #define BUFFER_SIZE 2048 27 | 28 | static void 29 | do_cat(const char *path) 30 | { 31 | int fd; 32 | unsigned char buf[BUFFER_SIZE]; 33 | int n; 34 | 35 | fd = open(path, O_RDONLY); 36 | if (fd < 0) die(path); 37 | for (;;) { 38 | n = read(fd, buf, sizeof buf); 39 | if (n < 0) die(path); 40 | if (n == 0) break; 41 | if (write(STDOUT_FILENO, buf, n) < 0) die(path); 42 | } 43 | if (close(fd) < 0) die(path); 44 | } 45 | 46 | static void 47 | die(const char *s) 48 | { 49 | perror(s); 50 | exit(1); 51 | } 52 | -------------------------------------------------------------------------------- /data.esc: -------------------------------------------------------------------------------- 1 | #include $ 2 | #include $ 3 | #include $ 4 | #include $ 5 | #include $ 6 | #include $ 7 | $ 8 | static void do_cat(const char *path);$ 9 | static void die(const char *s);$ 10 | $ 11 | int$ 12 | main(int argc, char *argv[])$ 13 | {$ 14 | int i;$ 15 | $ 16 | if (argc < 2) {$ 17 | \tfprintf(stderr, "%s: file name not given\n", argv[0]);$ 18 | \texit(1);$ 19 | }$ 20 | for (i = 1; i < argc; i++) {$ 21 | \tdo_cat(argv[i]);$ 22 | }$ 23 | exit(0);$ 24 | }$ 25 | $ 26 | #define BUFFER_SIZE 2048$ 27 | $ 28 | static void$ 29 | do_cat(const char *path)$ 30 | {$ 31 | int fd;$ 32 | unsigned char buf[BUFFER_SIZE];$ 33 | int n;$ 34 | $ 35 | fd = open(path, O_RDONLY);$ 36 | if (fd < 0) die(path);$ 37 | for (;;) {$ 38 | \tn = read(fd, buf, sizeof buf);$ 39 | \tif (n < 0) die(path);$ 40 | \tif (n == 0) break;$ 41 | \tif (write(STDOUT_FILENO, buf, n) < 0) die(path);$ 42 | }$ 43 | if (close(fd) < 0) die(path);$ 44 | }$ 45 | $ 46 | static void$ 47 | die(const char *s)$ 48 | {$ 49 | perror(s);$ 50 | exit(1);$ 51 | } -------------------------------------------------------------------------------- /head2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void do_head(FILE *f, long nlines); 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | long nlines; 10 | 11 | if (argc < 2) { 12 | fprintf(stderr, "Usage: %s n [file file...]\n", argv[0]); 13 | exit(1); 14 | } 15 | nlines = atol(argv[1]); 16 | if (argc == 2) { 17 | do_head(stdin, nlines); 18 | } else { /* else 절이 추가됨 */ 19 | int i; 20 | 21 | for (i = 2; i < argc; i++) { 22 | FILE *f; 23 | 24 | f = fopen(argv[i], "r"); 25 | if (!f) { 26 | perror(argv[i]); 27 | exit(1); 28 | } 29 | do_head(f, nlines); 30 | fclose(f); 31 | } 32 | } 33 | exit(0); 34 | } 35 | 36 | static void 37 | do_head(FILE *f, long nlines) 38 | { 39 | int c; 40 | 41 | if (nlines <= 0) return; 42 | while ((c = getc(f)) != EOF) { 43 | if (putchar(c) < 0) exit(1); 44 | if (c == '\n') { 45 | nlines--; 46 | if (nlines == 0) return; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spawn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | pid_t pid; 11 | 12 | if (argc != 3) { 13 | fprintf(stderr, "Usage: %s \n", argv[0]); 14 | exit(1); 15 | } 16 | pid = fork(); 17 | if (pid < 0) { 18 | fprintf(stderr, "fork(2) failed\n"); 19 | exit(1); 20 | } 21 | if (pid == 0) { /* 자식 프로세스 */ 22 | execl(argv[1], argv[1], argv[2], NULL); 23 | /* execl()는 성공하면 반환되지 않으므로, 함수가 반환된 경우는 전부 실패 */ 24 | perror(argv[1]); 25 | exit(99); 26 | } 27 | else { /* 부모 프로세스 */ 28 | int status; 29 | 30 | waitpid(pid, &status, 0); 31 | printf("child (PID=%d) finished; ", pid); 32 | if (WIFEXITED(status)) 33 | printf("exit, status=%d\n", WEXITSTATUS(status)); 34 | else if (WIFSIGNALED(status)) 35 | printf("signal, sig=%d\n", WTERMSIG(status)); 36 | else 37 | printf("abnormal exit\n"); 38 | exit(0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSES: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Minero Aoki 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g -Wall -Wno-parentheses 3 | CPPFLAGS = 4 | TARGETS = hello echo segv args bell \ 5 | cat0 cat cat2 cat3 cat-escape cat4 cat5 getcperf feof-bug \ 6 | wc-l-stdio wc-l-syscall \ 7 | head head2 head3 head4 \ 8 | tail tail2 \ 9 | grep grep2 grep3 array wgrep slice slice2 \ 10 | ls ls2 catdir rm mkdir mkpath rmdir ln symlink mv stat touch chmod chown chgrp traverse \ 11 | mapwrite \ 12 | exec spawn dupread sh1 sh2 \ 13 | sig sigqueue-test isatty trap \ 14 | pwd pwd2 pwd3 env showenv user id timefmt strftime \ 15 | daytime daytimed echoclient \ 16 | httpd httpd2 logging 17 | 18 | DLLIB = -ldl 19 | NETLIB = 20 | 21 | .SUFFIXES: 22 | .SUFFIXES: .c . 23 | .c: 24 | $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ 25 | 26 | all: $(TARGETS) 27 | 28 | show-vmmap: show-vmmap.c 29 | $(CC) $(CFLAGS) $(CPPFLAGS) show-vmmap.c $(DLLIB) -o $@ 30 | 31 | daytime: daytime.c 32 | $(CC) $(CFLAGS) daytime.c $(NETLIB) -o $@ 33 | 34 | daytimed: daytimed.c 35 | $(CC) $(CFLAGS) daytimed.c $(NETLIB) -o $@ 36 | 37 | test: all 38 | @sh test-scripts.sh 39 | 40 | clean: 41 | rm -f $(TARGETS) 42 | -------------------------------------------------------------------------------- /tail.c: -------------------------------------------------------------------------------- 1 | /* 2 | tail -- Shows last 10 lines of files (with line length limitation) 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #define N_LINES 10 9 | #define MAX_LINE_LENGTH 1024 10 | 11 | static void tail(FILE *f); 12 | 13 | int 14 | main(int argc, char *argv[]) 15 | { 16 | if (argc != 1) { 17 | fprintf(stderr, "Usage: %s < infile\n", argv[0]); 18 | exit(1); 19 | } 20 | tail(stdin); 21 | exit(0); 22 | } 23 | 24 | static char ring_buffer[N_LINES][MAX_LINE_LENGTH]; 25 | 26 | #define RING_BUFFER_INC(idx) do { \ 27 | idx++; \ 28 | if (idx >= N_LINES) { \ 29 | idx = 0; \ 30 | } \ 31 | } while (0) 32 | 33 | static void 34 | tail(FILE *f) 35 | { 36 | long curr = 0; 37 | long n_read_lines = 0; 38 | 39 | // Read all lines into ring buffer 40 | while (fgets(ring_buffer[curr], MAX_LINE_LENGTH, f)) { 41 | RING_BUFFER_INC(curr); 42 | n_read_lines++; 43 | } 44 | 45 | // Rewind ring buffer 46 | if (n_read_lines < N_LINES) { 47 | curr = 0; 48 | } 49 | 50 | // Flush ring buffer 51 | int n; 52 | for (n = N_LINES; n > 0 && n_read_lines; n--, n_read_lines--) { 53 | printf("%s", ring_buffer[curr]); 54 | RING_BUFFER_INC(curr); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cat-escape.c: -------------------------------------------------------------------------------- 1 | /* 2 | cat-escape.c -- cat command which escapes \t and \n 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | static void do_cat(FILE *f); 15 | 16 | int 17 | main(int argc, char *argv[]) 18 | { 19 | if (argc == 1) { 20 | do_cat(stdin); 21 | } 22 | else { 23 | int i; 24 | 25 | for (i = 1; i < argc; i++) { 26 | FILE *f = fopen(argv[i], "r"); 27 | if (!f) { 28 | perror(argv[i]); 29 | exit(1); 30 | } 31 | do_cat(f); 32 | fclose(f); 33 | } 34 | } 35 | exit(0); 36 | } 37 | 38 | static void 39 | do_cat(FILE *f) 40 | { 41 | int c; 42 | 43 | while ((c = fgetc(f)) != EOF) { 44 | switch (c) { 45 | case '\t': 46 | if (fputs("\\t", stdout) == EOF) exit(1); 47 | break; 48 | case '\n': 49 | if (fputs("$\n", stdout) == EOF) exit(1); 50 | break; 51 | default: 52 | if (putchar(c) < 0) exit(1); 53 | break; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /grep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void do_grep(regex_t *pat, FILE *f); 8 | 9 | int 10 | main(int argc, char *argv[]) 11 | { 12 | regex_t pat; 13 | int err; 14 | int i; 15 | 16 | if (argc < 2) { 17 | fputs("no pattern\n", stderr); 18 | exit(1); 19 | } 20 | err = regcomp(&pat, argv[1], REG_EXTENDED | REG_NOSUB | REG_NEWLINE); 21 | if (err != 0) { 22 | char buf[1024]; 23 | 24 | regerror(err, &pat, buf, sizeof buf); 25 | puts(buf); 26 | exit(1); 27 | } 28 | if (argc == 2) { 29 | do_grep(&pat, stdin); 30 | } 31 | else { 32 | for (i = 2; i < argc; i++) { 33 | FILE *f; 34 | 35 | f = fopen(argv[i], "r"); 36 | if (!f) { 37 | perror(argv[i]); 38 | exit(1); 39 | } 40 | do_grep(&pat, f); 41 | fclose(f); 42 | } 43 | } 44 | regfree(&pat); 45 | exit(0); 46 | } 47 | 48 | static void 49 | do_grep(regex_t *pat, FILE *src) 50 | { 51 | char buf[4096]; 52 | 53 | while (fgets(buf, sizeof buf, src)) { 54 | if (regexec(pat, buf, 0, NULL, 0) == 0) { 55 | fputs(buf, stdout); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /head3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void do_head(FILE *f, long nlines); 6 | 7 | #define DEFAULT_N_LINES 10 8 | 9 | int 10 | main(int argc, char *argv[]) 11 | { 12 | int opt; 13 | long nlines = DEFAULT_N_LINES; 14 | 15 | while ((opt = getopt(argc, argv, "n:")) != -1) { 16 | switch (opt) { 17 | case 'n': 18 | nlines = atol(optarg); 19 | break; 20 | case '?': 21 | fprintf(stderr, "Usage: %s [-n LINES] [file...]\n", argv[0]); 22 | exit(1); 23 | } 24 | } 25 | if (optind == argc) { 26 | do_head(stdin, nlines); 27 | } else { 28 | int i; 29 | 30 | for (i = optind; i < argc; i++) { 31 | FILE *f; 32 | 33 | f = fopen(argv[i], "r"); 34 | if (!f) { 35 | perror(argv[i]); 36 | exit(1); 37 | } 38 | do_head(f, nlines); 39 | fclose(f); 40 | } 41 | } 42 | exit(0); 43 | } 44 | 45 | static void 46 | do_head(FILE *f, long nlines) 47 | { 48 | int c; 49 | 50 | if (nlines <= 0) return; 51 | while ((c = getc(f)) != EOF) { 52 | if (putchar(c) < 0) exit(1); 53 | if (c == '\n') { 54 | nlines--; 55 | if (nlines == 0) return; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /wc-l-syscall.c: -------------------------------------------------------------------------------- 1 | /* 2 | wc-l-syscall.c -- Simple "wc -l" command (system call version) 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static void do_word_count(int fd, const char *path); 13 | static void die(const char *s); 14 | 15 | int 16 | main(int argc, char *argv[]) 17 | { 18 | int i; 19 | 20 | if (argc < 2) { 21 | fprintf(stderr, "%s: file name not given\n", argv[0]); 22 | exit(1); 23 | } 24 | for (i = 1; i < argc; i++) { 25 | char *path = argv[i]; 26 | int fd = open(path, O_RDONLY); 27 | if (fd < 0) die(path); 28 | do_word_count(fd, path); 29 | if (close(fd) < 0) die(path); 30 | } 31 | exit(0); 32 | } 33 | 34 | #define BUFFER_SIZE 2048 35 | 36 | static void 37 | do_word_count(int fd, const char *path) 38 | { 39 | unsigned long count = 0; 40 | for (;;) { 41 | unsigned char buf[BUFFER_SIZE]; 42 | int n = read(fd, buf, sizeof buf); 43 | if (n < 0) die(path); 44 | if (n == 0) break; 45 | unsigned long i; 46 | for (i = 0; i < BUFFER_SIZE; i++) { 47 | if (buf[i] == '\n') { 48 | count++; 49 | } 50 | } 51 | } 52 | printf("%lu\n", count); 53 | } 54 | 55 | static void 56 | die(const char *s) 57 | { 58 | perror(s); 59 | exit(1); 60 | } 61 | -------------------------------------------------------------------------------- /id.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static char * user_name(uid_t id); 9 | static char * group_name(gid_t id); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | uid_t u; 15 | gid_t g; 16 | char *name; 17 | gid_t *buf; 18 | long group_max; 19 | int n; 20 | char *comma; 21 | long i; 22 | 23 | u = getuid(); 24 | printf("uid=%d", u); 25 | if (name = user_name(u)) { 26 | printf("(%s)", name); 27 | } 28 | 29 | g = getgid(); 30 | printf(" gid=%d", g); 31 | if (name = group_name(g)) { 32 | printf("(%s)", name); 33 | } 34 | 35 | group_max = sysconf(_SC_NGROUPS_MAX); 36 | buf = malloc(sizeof(gid_t) * group_max); 37 | if (!buf) { 38 | fprintf(stderr, "malloc failed\n"); 39 | exit(1); 40 | } 41 | n = getgroups(group_max, buf); 42 | if (n < 0) { 43 | perror("getgroups"); 44 | exit(1); 45 | } 46 | printf(" groups="); 47 | comma = ""; 48 | for (i = 0; i < n; i++) { 49 | printf("%s%d(%s)", comma, buf[i], group_name(buf[i])); 50 | comma = ","; 51 | } 52 | printf("\n"); 53 | 54 | exit(0); 55 | } 56 | 57 | static char * 58 | user_name(uid_t id) 59 | { 60 | struct passwd *pw; 61 | 62 | pw = getpwuid(id); 63 | if (!pw) return NULL; 64 | return pw->pw_name; 65 | } 66 | 67 | static char * 68 | group_name(gid_t id) 69 | { 70 | struct group *gr; 71 | 72 | gr = getgrgid(id); 73 | if (!gr) return NULL; 74 | return gr->gr_name; 75 | } 76 | -------------------------------------------------------------------------------- /daytime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int open_connection(char *host, char *service); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | int sock; 15 | FILE *f; 16 | char buf[1024]; 17 | 18 | sock = open_connection((argc>1 ? argv[1] : "localhost"), "daytime"); 19 | f = fdopen(sock, "r"); 20 | if (!f) { 21 | perror("fdopen(3)"); 22 | exit(1); 23 | } 24 | fgets(buf, sizeof buf, f); 25 | fclose(f); 26 | fputs(buf, stdout); 27 | exit(0); 28 | } 29 | 30 | static int 31 | open_connection(char *host, char *service) 32 | { 33 | int sock; 34 | struct addrinfo hints, *res, *ai; 35 | int err; 36 | 37 | memset(&hints, 0, sizeof(struct addrinfo)); 38 | hints.ai_family = AF_UNSPEC; 39 | hints.ai_socktype = SOCK_STREAM; 40 | if ((err = getaddrinfo(host, service, &hints, &res)) != 0) { 41 | fprintf(stderr, "getaddrinfo(3): %s\n", gai_strerror(err)); 42 | exit(1); 43 | } 44 | for (ai = res; ai; ai = ai->ai_next) { 45 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 46 | if (sock < 0) { 47 | continue; 48 | } 49 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 50 | close(sock); 51 | continue; 52 | } 53 | /* success */ 54 | freeaddrinfo(res); 55 | return sock; 56 | } 57 | fprintf(stderr, "socket(2)/connect(2) failed"); 58 | freeaddrinfo(res); 59 | exit(1); 60 | } 61 | -------------------------------------------------------------------------------- /stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static char *filetype(mode_t mode); 8 | 9 | int 10 | main(int argc, char *argv[]) 11 | { 12 | struct stat st; 13 | 14 | if (argc != 2) { 15 | fprintf(stderr, "wrong argument\n"); 16 | exit(1); 17 | } 18 | if (lstat(argv[1], &st) < 0) { 19 | perror(argv[1]); 20 | exit(1); 21 | } 22 | printf("type\t%o (%s)\n", (st.st_mode & S_IFMT), filetype(st.st_mode)); 23 | printf("mode\t%o\n", st.st_mode & ~S_IFMT); 24 | printf("dev\t%llu\n", (unsigned long long)st.st_dev); 25 | printf("ino\t%lu\n", (unsigned long)st.st_ino); 26 | printf("rdev\t%llu\n", (unsigned long long)st.st_rdev); 27 | printf("nlink\t%lu\n", (unsigned long)st.st_nlink); 28 | printf("uid\t%d\n", st.st_uid); 29 | printf("gid\t%d\n", st.st_gid); 30 | printf("size\t%ld\n", st.st_size); 31 | printf("blksize\t%lu\n", (unsigned long)st.st_blksize); 32 | printf("blocks\t%lu\n", (unsigned long)st.st_blocks); 33 | printf("atime\t%s", ctime(&st.st_atime)); 34 | printf("mtime\t%s", ctime(&st.st_mtime)); 35 | printf("ctime\t%s", ctime(&st.st_ctime)); 36 | exit(0); 37 | } 38 | 39 | static char* 40 | filetype(mode_t mode) 41 | { 42 | if (S_ISREG(mode)) return "file"; 43 | if (S_ISDIR(mode)) return "directory"; 44 | if (S_ISCHR(mode)) return "chardev"; 45 | if (S_ISBLK(mode)) return "blockdev"; 46 | if (S_ISFIFO(mode)) return "fifo"; 47 | if (S_ISLNK(mode)) return "symlink"; 48 | if (S_ISSOCK(mode)) return "socket"; 49 | return "unknown"; 50 | } 51 | -------------------------------------------------------------------------------- /catdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | 9 | struct linux_dirent { 10 | long d_ino; 11 | off_t d_off; 12 | unsigned short d_reclen; 13 | char d_name[]; 14 | /* 15 | char pad; 16 | char d_type; 17 | */ 18 | }; 19 | 20 | #ifdef PRETTY_PRINT 21 | static void print_dirents(char *buf, size_t len); 22 | #endif 23 | 24 | int 25 | main(int argc, char *argv[]) 26 | { 27 | int fd; 28 | 29 | if (argc < 2) { 30 | fprintf(stderr, "missing dirname\n"); 31 | exit(1); 32 | } 33 | fd = open(argv[1], O_RDONLY); 34 | if (fd < 0) { 35 | perror(argv[1]); 36 | exit(1); 37 | } 38 | for (;;) { 39 | char buf[1024]; 40 | int n; 41 | 42 | n = syscall(__NR_getdents, fd, buf, sizeof buf); 43 | if (n < 0) { 44 | perror("getdents(2)"); 45 | exit(1); 46 | } 47 | if (n == 0) break; 48 | #ifdef PRETTY_PRINT 49 | print_dirents(buf, n); 50 | #else 51 | write(STDOUT_FILENO, buf, n); 52 | #endif 53 | } 54 | exit(0); 55 | } 56 | 57 | #ifdef PRETTY_PRINT 58 | static void 59 | print_dirents(char *buf, size_t len) { 60 | int offset; 61 | 62 | for (offset = 0; offset < len; ) { 63 | struct linux_dirent *ent = (struct linux_dirent*)(buf + offset); 64 | printf("d_ino=%ld, d_off=%ld, d_reclen=%u, d_name=%s\n", ent->d_ino, (long)ent->d_off, (unsigned int)ent->d_reclen, ent->d_name); 65 | offset += ent->d_reclen; 66 | } 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /wgrep.c: -------------------------------------------------------------------------------- 1 | /* 2 | GREP with wide character support. 3 | This program handles only immediate pattern. 4 | Set environment variable LANG before invokind this command. 5 | 6 | e.g. LANG=ja_JP.eucJP ./wgrep PATTERN FILE... 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static void do_grep(wchar_t *pattern, FILE *f); 17 | 18 | int 19 | main(int argc, char *argv[]) 20 | { 21 | wchar_t pattern[1024]; 22 | int i; 23 | 24 | setlocale(LC_CTYPE, ""); 25 | if (argc < 2) { 26 | fputs("no pattern\n", stderr); 27 | exit(1); 28 | } 29 | if (mbstowcs(pattern, argv[1], 1024) == (size_t)(-1)) { 30 | fprintf(stderr, "mbstowcs failed #1\n"); 31 | exit(1); 32 | } 33 | if (argc == 2) { 34 | do_grep(pattern, stdin); 35 | } 36 | else { 37 | for (i = 2; i < argc; i++) { 38 | FILE *f; 39 | 40 | f = fopen(argv[i], "r"); 41 | if (!f) { 42 | perror(argv[i]); 43 | exit(1); 44 | } 45 | do_grep(pattern, f); 46 | fclose(f); 47 | } 48 | } 49 | exit(0); 50 | } 51 | 52 | static void 53 | do_grep(wchar_t *pattern, FILE *src) 54 | { 55 | char buf[4096]; 56 | wchar_t wbuf[4096]; 57 | 58 | while (fgets(buf, sizeof buf, src)) { 59 | if (mbstowcs(wbuf, buf, 4096) == (size_t)(-1)) { 60 | fprintf(stderr, "mbstowcs failed #2\n"); 61 | exit(1); 62 | } 63 | if (wcsstr(wbuf, pattern)) { 64 | fputs(buf, stdout); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /strftime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | time_t t; 11 | struct tm *tm; 12 | char *fmtchars = "aAbBcCdDeFgGhHIjklmMnOpPrRsStTuUVwWxXyYzZ+"; 13 | char *p; 14 | char *opt_E = "cCxXyY"; 15 | char *opt_O = "deHImMSuUvwWy"; 16 | 17 | setlocale(LC_ALL, ""); 18 | time(&t); 19 | tm = localtime(&t); 20 | for (p = fmtchars; *p; p++) { 21 | char fmt[16]; 22 | char buf[256]; 23 | 24 | printf("%%%c=", *p); 25 | fmt[0] = '"'; 26 | fmt[1] = '%'; 27 | fmt[2] = *p; 28 | fmt[3] = '"'; 29 | fmt[4] = '\0'; 30 | if (strftime(buf, sizeof buf, fmt, tm) == 0) 31 | puts("FAILED"); 32 | else 33 | puts(buf); 34 | 35 | if (strchr(opt_E, *p)) { 36 | printf("%%E%c=", *p); 37 | fmt[0] = '"'; 38 | fmt[1] = '%'; 39 | fmt[2] = 'E'; 40 | fmt[3] = *p; 41 | fmt[4] = '"'; 42 | fmt[5] = '\0'; 43 | if (strftime(buf, sizeof buf, fmt, tm) == 0) 44 | puts("FAILED"); 45 | else 46 | puts(buf); 47 | } 48 | if (strchr(opt_O, *p)) { 49 | printf("%%O%c=", *p); 50 | fmt[0] = '"'; 51 | fmt[1] = '%'; 52 | fmt[2] = 'O'; 53 | fmt[3] = *p; 54 | fmt[4] = '"'; 55 | fmt[5] = '\0'; 56 | if (strftime(buf, sizeof buf, fmt, tm) == 0) 57 | puts("FAILED"); 58 | else 59 | puts(buf); 60 | } 61 | } 62 | exit(0); 63 | } 64 | -------------------------------------------------------------------------------- /gdb/head.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define _GNU_SOURCE 6 | #include 7 | 8 | static void do_head(FILE *f, int nlines); 9 | 10 | #define DEFAULT_N_LINES 10 11 | 12 | static struct option longopts[] = { 13 | {"lines", required_argument, NULL, 'n'}, 14 | {"help", no_argument, NULL, 'h'}, 15 | {0, 0, 0, 0} 16 | }; 17 | 18 | int 19 | main(int argc, char *argv[]) 20 | { 21 | int opt; 22 | int nlines = DEFAULT_N_LINES; 23 | 24 | while ((opt = getopt_long(argc, argv, "n", longopts, NULL)) != -1) { 25 | switch (opt) { 26 | case 'n': 27 | nlines = atoi(optarg); 28 | break; 29 | case 'h': 30 | fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]); 31 | exit(0); 32 | case '?': 33 | fprintf(stderr, "%s: unknown option: -%c\n", argv[0], optopt); 34 | exit(1); 35 | } 36 | } 37 | if (optind == argc) { 38 | do_head(stdin, nlines); 39 | } else { 40 | int i; 41 | 42 | for (i = optind; i < argc; i++) { 43 | FILE *f; 44 | 45 | f = fopen(argv[i], "r"); 46 | if (!f) { 47 | perror(argv[i]); 48 | exit(1); 49 | } 50 | do_head(f, nlines); 51 | fclose(f); 52 | } 53 | } 54 | exit(0); 55 | } 56 | 57 | static void 58 | do_head(FILE *f, int nlines) 59 | { 60 | int c; 61 | 62 | if (nlines == 0) return; 63 | while ((c = getc(f)) != EOF) { 64 | if (putchar(c) < 0) exit(1); 65 | if (c == '\n') { 66 | nlines--; 67 | if (nlines == 0) 68 | return; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /slice.c: -------------------------------------------------------------------------------- 1 | /* 2 | slice - Prints matched substrings (with line length limitation) 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static void do_slice(regex_t *pat, FILE *f); 18 | 19 | int 20 | main(int argc, char *argv[]) 21 | { 22 | regex_t pat; 23 | int err; 24 | int i; 25 | 26 | if (argc < 2) { 27 | fputs("no pattern\n", stderr); 28 | exit(1); 29 | } 30 | err = regcomp(&pat, argv[1], REG_EXTENDED | REG_NEWLINE); 31 | if (err != 0) { 32 | char buf[1024]; 33 | 34 | regerror(err, &pat, buf, sizeof buf); 35 | puts(buf); 36 | exit(1); 37 | } 38 | if (argc == 2) { 39 | do_slice(&pat, stdin); 40 | } 41 | else { 42 | for (i = 2; i < argc; i++) { 43 | FILE *f; 44 | 45 | f = fopen(argv[i], "r"); 46 | if (!f) { 47 | perror(argv[i]); 48 | exit(1); 49 | } 50 | do_slice(&pat, f); 51 | fclose(f); 52 | } 53 | } 54 | regfree(&pat); 55 | exit(0); 56 | } 57 | 58 | static void 59 | do_slice(regex_t *pat, FILE *src) 60 | { 61 | char buf[4096]; 62 | 63 | while (fgets(buf, sizeof buf, src)) { 64 | regmatch_t matched[1]; 65 | if (regexec(pat, buf, 1, matched, 0) == 0) { 66 | char *str = buf + matched[0].rm_so; 67 | regoff_t len = matched[0].rm_eo - matched[0].rm_so; 68 | fwrite(str, len, 1, stdout); 69 | fputc('\n', stdout); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /head4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* (1) */ 4 | #define _GNU_SOURCE 5 | #include 6 | 7 | static void do_head(FILE *f, long nlines); 8 | 9 | #define DEFAULT_N_LINES 10 10 | 11 | /* (2) */ 12 | static struct option longopts[] = { 13 | {"lines", required_argument, NULL, 'n'}, 14 | {"help", no_argument, NULL, 'h'}, 15 | {0, 0, 0, 0} 16 | }; 17 | 18 | int 19 | main(int argc, char *argv[]) 20 | { 21 | int opt; 22 | long nlines = DEFAULT_N_LINES; 23 | 24 | /* (3) */ 25 | while ((opt = getopt_long(argc, argv, "n:", longopts, NULL)) != -1) { 26 | switch (opt) { 27 | case 'n': /* (4) */ 28 | nlines = atol(optarg); 29 | break; 30 | case 'h': /* (5) */ 31 | fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]); 32 | exit(0); 33 | case '?': /* (6) */ 34 | fprintf(stderr, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]); 35 | exit(1); 36 | } 37 | } 38 | /* (7) */ 39 | if (optind == argc) { 40 | do_head(stdin, nlines); 41 | } else { 42 | int i; 43 | 44 | /* (7') */ 45 | for (i = optind; i < argc; i++) { 46 | FILE *f; 47 | 48 | f = fopen(argv[i], "r"); 49 | if (!f) { 50 | perror(argv[i]); 51 | exit(1); 52 | } 53 | do_head(f, nlines); 54 | fclose(f); 55 | } 56 | } 57 | exit(0); 58 | } 59 | 60 | static void 61 | do_head(FILE *f, long nlines) 62 | { 63 | int c; 64 | 65 | if (nlines <= 0) return; 66 | while ((c = getc(f)) != EOF) { 67 | if (putchar(c) < 0) exit(1); 68 | if (c == '\n') { 69 | nlines--; 70 | if (nlines == 0) return; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /echoclient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int open_connection(char *host, char *service); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | if (argc != 3) { 15 | fprintf(stderr, "%s: error: wrong number of argument: %d for 2\n", argv[0], argc - 1); 16 | fprintf(stderr, "Usage: %s HOST MESSAGE\n", argv[0]); 17 | exit(1); 18 | } 19 | char *host = argv[1]; 20 | char *msg = argv[2]; 21 | 22 | int sock = open_connection(host, "echo"); 23 | FILE *f = fdopen(sock, "w+"); 24 | if (!f) { 25 | perror("fdopen(3)"); 26 | exit(1); 27 | } 28 | 29 | fprintf(f, "%s\n", msg); 30 | fflush(f); 31 | 32 | char buf[1024]; 33 | fgets(buf, sizeof buf, f); 34 | fclose(f); 35 | fputs(buf, stdout); 36 | exit(0); 37 | } 38 | 39 | static int 40 | open_connection(char *host, char *service) 41 | { 42 | struct addrinfo hints, *res, *ai; 43 | int err; 44 | 45 | memset(&hints, 0, sizeof(struct addrinfo)); 46 | hints.ai_family = AF_UNSPEC; 47 | hints.ai_socktype = SOCK_STREAM; 48 | if ((err = getaddrinfo(host, service, &hints, &res)) != 0) { 49 | fprintf(stderr, "getaddrinfo(3): %s\n", gai_strerror(err)); 50 | exit(1); 51 | } 52 | for (ai = res; ai; ai = ai->ai_next) { 53 | int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 54 | if (sock < 0) { 55 | continue; 56 | } 57 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 58 | close(sock); 59 | continue; 60 | } 61 | /* success */ 62 | freeaddrinfo(res); 63 | return sock; 64 | } 65 | fprintf(stderr, "socket(2)/connect(2) failed"); 66 | freeaddrinfo(res); 67 | exit(1); 68 | } 69 | -------------------------------------------------------------------------------- /cat4.c: -------------------------------------------------------------------------------- 1 | /* 2 | cat4.c -- simple cat command with -e option 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | static void do_cat(FILE *f, int escape); 16 | 17 | int 18 | main(int argc, char *argv[]) 19 | { 20 | int opt; 21 | int escape = 0; 22 | int i; 23 | 24 | while ((opt = getopt(argc, argv, "e")) != -1) { 25 | switch (opt) { 26 | case 'e': 27 | escape = 1; 28 | break; 29 | case '?': 30 | fprintf(stderr, "Usage: %s [-e] [file...]\n", argv[0]); 31 | exit(1); 32 | } 33 | } 34 | argc -= optind; 35 | argv += optind; 36 | if (argc == 0) { 37 | do_cat(stdin, escape); 38 | } 39 | else { 40 | for (i = 0; i < argc; i++) { 41 | FILE *f; 42 | 43 | f = fopen(argv[i], "r"); 44 | if (!f) { 45 | perror(argv[i]); 46 | exit(1); 47 | } 48 | do_cat(f, escape); 49 | fclose(f); 50 | } 51 | } 52 | exit(0); 53 | } 54 | 55 | static void 56 | do_cat(FILE *f, int escape) 57 | { 58 | int c; 59 | 60 | if (escape) { 61 | while ((c = fgetc(f)) != EOF) { 62 | switch (c) { 63 | case '\t': 64 | if (fputs("\\t", stdout) == EOF) exit(1); 65 | break; 66 | case '\n': 67 | if (fputs("$\n", stdout) == EOF) exit(1); 68 | break; 69 | default: 70 | if (putchar(c) < 0) exit(1); 71 | break; 72 | } 73 | } 74 | } 75 | else { 76 | while ((c = fgetc(f)) != EOF) { 77 | if (putchar(c) < 0) exit(1); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern char **environ; 8 | 9 | static void 10 | die(char *msg) 11 | { 12 | printf("--- %s error ---\n", msg); 13 | exit(1); 14 | } 15 | 16 | int 17 | main(int argc, char *argv[]) 18 | { 19 | puts("<>"); 20 | if (fork() != 0) { /* parent */ 21 | wait(NULL); 22 | } 23 | else { /* child */ 24 | execl("./args", "PROGNAME", "called by execl", NULL); 25 | die("execl"); 26 | } 27 | 28 | puts("<>"); 29 | if (fork() != 0) { /* parent */ 30 | wait(NULL); 31 | } 32 | else { /* child */ 33 | execlp("./args", "PROGNAME", "called by execlp", NULL); 34 | die("execlp"); 35 | } 36 | 37 | puts("<>"); 38 | if (fork() != 0) { /* parent */ 39 | wait(NULL); 40 | } 41 | else { /* child */ 42 | execle("./args", "PROGNAME", "called by execle", NULL, environ); 43 | die("execle"); 44 | } 45 | 46 | puts("<>"); 47 | if (fork() != 0) { /* parent */ 48 | wait(NULL); 49 | } 50 | else { /* child */ 51 | char *args[4] = { "PROGNAME", "called by execv", NULL }; 52 | execv("./args", args); 53 | die("execv"); 54 | } 55 | 56 | puts("<>"); 57 | if (fork() != 0) { /* parent */ 58 | wait(NULL); 59 | } 60 | else { /* child */ 61 | char *args[4] = { "PROGNAME", "called by execvp", NULL }; 62 | execvp("./args", args); 63 | die("execvp"); 64 | } 65 | 66 | puts("<>"); 67 | if (fork() != 0) { /* parent */ 68 | wait(NULL); 69 | } 70 | else { /* child */ 71 | char *args[4] = { "PROGNAME", "called by execve", NULL }; 72 | execve("./args", args, environ); 73 | die("execve"); 74 | } 75 | 76 | exit(0); 77 | } 78 | -------------------------------------------------------------------------------- /tail2.c: -------------------------------------------------------------------------------- 1 | /* 2 | tail2 -- simple tail command with no line length limitation 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | static void tail(FILE *f, int nlines); 9 | static unsigned char *readline(FILE *f); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | if (argc != 2) { 15 | fprintf(stderr, "Usage: %s n\n", argv[0]); 16 | exit(1); 17 | } 18 | tail(stdin, atoi(argv[1])); 19 | exit(0); 20 | } 21 | 22 | static void 23 | tail(FILE *f, int nlines) 24 | { 25 | unsigned char **ringbuf; 26 | unsigned char **p; 27 | unsigned char *line; 28 | int n; 29 | 30 | #define INC(ptrvar) do { \ 31 | ptrvar++; \ 32 | if (ptrvar >= ringbuf + nlines) \ 33 | ptrvar = ringbuf; \ 34 | } while (0) 35 | 36 | if (nlines == 0) return; 37 | 38 | ringbuf = calloc(nlines, sizeof(char*)); 39 | if (ringbuf == NULL) exit(1); 40 | p = ringbuf; 41 | while (line = readline(f)) { 42 | if (*p) 43 | free(*p); 44 | *p = line; 45 | INC(p); 46 | } 47 | if (*p == NULL) p = ringbuf; 48 | for (n = nlines; n > 0 && *p; n--) { 49 | printf("%s", *p); 50 | free(*p); 51 | INC(p); 52 | } 53 | free(ringbuf); 54 | } 55 | 56 | static unsigned char * 57 | readline(FILE *f) 58 | { 59 | unsigned char *buf, *p; 60 | size_t buflen = BUFSIZ; 61 | int c; 62 | 63 | buf = p = malloc(sizeof(char) * buflen); 64 | if (buf == NULL) exit(1); 65 | for (;;) { 66 | c = getc(f); 67 | if (c == EOF) { 68 | if (buf == p) { 69 | free(buf); 70 | return NULL; 71 | } 72 | break; 73 | } 74 | *p++ = c; 75 | if (p >= buf + buflen - 1) { 76 | buflen *= 2; 77 | buf = realloc(buf, buflen); 78 | if (buf == NULL) exit(1); 79 | } 80 | if (c == '\n') break; 81 | } 82 | *p++ = '\0'; 83 | return buf; 84 | } 85 | -------------------------------------------------------------------------------- /ls2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void do_ls(char *path); 11 | static void die(const char *s); 12 | static char* allocate_path_buffer(size_t required_len); 13 | 14 | int 15 | main(int argc, char *argv[]) 16 | { 17 | int i; 18 | 19 | if (argc < 2) { 20 | fprintf(stderr, "%s: no arguments\n", argv[0]); 21 | exit(1); 22 | } 23 | for (i = 1; i < argc; i++) { 24 | do_ls(argv[i]); 25 | } 26 | exit(0); 27 | } 28 | 29 | static void 30 | do_ls(char *path) 31 | { 32 | DIR *d = opendir(path); 33 | if (!d) { 34 | perror(path); 35 | exit(1); 36 | } 37 | struct dirent *ent; 38 | while (ent = readdir(d)) { 39 | char *buf = allocate_path_buffer(strlen(path) + 1 + strlen(ent->d_name)); 40 | // sprintf does NOT cause buffer overflow here, because we calculate required buffer length beforehand. 41 | sprintf(buf, "%s/%s", path, ent->d_name); 42 | 43 | struct stat st; 44 | if (lstat(buf, &st) < 0) die(buf); 45 | char *mtime = ctime(&st.st_mtime); 46 | mtime[strlen(mtime) - 1] = '\0'; // ctime returns a string terminated by '\n', remove it 47 | 48 | struct passwd *pw = getpwuid(st.st_uid); 49 | if (pw) { // passwd entry found 50 | printf("%s owner=%s mtime=%s\n", ent->d_name, pw->pw_name, mtime); 51 | } 52 | else { 53 | printf("%s owner=%d mtime=%s\n", ent->d_name, st.st_uid, mtime); 54 | } 55 | } 56 | closedir(d); 57 | } 58 | 59 | static char *file_path = NULL; 60 | size_t path_len = 0; 61 | 62 | static char* 63 | allocate_path_buffer(size_t required_len) 64 | { 65 | size_t len = path_len; 66 | while (len < required_len) { 67 | len += 1024; 68 | } 69 | if (len > path_len) { 70 | if (!file_path) { 71 | file_path = malloc(len); 72 | if (!file_path) die("malloc"); 73 | } 74 | else { 75 | char *tmp = realloc(file_path, len); 76 | if (!tmp) die("realloc"); 77 | file_path = tmp; 78 | } 79 | } 80 | return file_path; 81 | } 82 | 83 | static void 84 | die(const char *s) 85 | { 86 | perror(s); 87 | exit(1); 88 | } 89 | -------------------------------------------------------------------------------- /grep3.c: -------------------------------------------------------------------------------- 1 | /* 2 | grep3.c -- grep command without line length limitation 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static void do_grep(regex_t *pat, FILE *f); 18 | static char* get_line(FILE *f); 19 | static void die(const char *s); 20 | 21 | int 22 | main(int argc, char *argv[]) 23 | { 24 | regex_t pat; 25 | int err; 26 | int i; 27 | 28 | if (argc < 2) { 29 | fputs("no pattern\n", stderr); 30 | exit(1); 31 | } 32 | err = regcomp(&pat, argv[1], REG_EXTENDED | REG_NOSUB | REG_NEWLINE); 33 | if (err != 0) { 34 | char buf[1024]; 35 | 36 | regerror(err, &pat, buf, sizeof buf); 37 | puts(buf); 38 | exit(1); 39 | } 40 | if (argc == 2) { 41 | do_grep(&pat, stdin); 42 | } 43 | else { 44 | for (i = 2; i < argc; i++) { 45 | FILE *f = fopen(argv[i], "r"); 46 | if (!f) die(argv[i]); 47 | do_grep(&pat, f); 48 | fclose(f); 49 | } 50 | } 51 | regfree(&pat); 52 | exit(0); 53 | } 54 | 55 | static void 56 | do_grep(regex_t *pat, FILE *src) 57 | { 58 | char *line; 59 | 60 | while (line = get_line(src)) { 61 | if (regexec(pat, line, 0, NULL, 0) == 0) { 62 | puts(line); 63 | } 64 | free(line); 65 | } 66 | } 67 | 68 | static char* 69 | get_line(FILE *src) 70 | { 71 | unsigned char *line; /* 버퍼 */ 72 | size_t capa = 160; /* 버퍼의 크기 */ 73 | size_t idx = 0; /* 현재 버퍼에 쓰는 위치 */ 74 | int c; 75 | 76 | line = malloc(capa); 77 | if (!line) die("malloc"); 78 | while ((c = getc(src)) != EOF) { 79 | if (c == '\n') 80 | break; 81 | if (idx == capa - 1) { /* 버퍼의 길이 체크 */ 82 | capa *= 2; 83 | line = realloc(line, capa); 84 | if (!line) die("realloc"); 85 | } 86 | line[idx++] = (unsigned char)c; 87 | } 88 | if ((idx == 0) && (c == EOF)) { 89 | free(line); 90 | return NULL; 91 | } 92 | line[idx++] = '\0'; 93 | 94 | return (char*)line; 95 | } 96 | 97 | static void 98 | die(const char *s) 99 | { 100 | perror(s); 101 | exit(1); 102 | } 103 | -------------------------------------------------------------------------------- /daytimed.c: -------------------------------------------------------------------------------- 1 | /* 2 | daytimed.c -- Simple daytime server. 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #if defined(__digital__) && defined(__unix__) 12 | # ifdef _XOPEN_SOURCE 13 | # undef _XOPEN_SOURCE 14 | # endif 15 | # define _XOPEN_SOURCE 500 16 | # define _OSF_SOURCE 17 | #endif 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define DEFAULT_PORT 13 28 | 29 | static int listen_socket(int port); 30 | 31 | int 32 | main(int argc, char *argv[]) 33 | { 34 | struct sockaddr_storage addr; 35 | socklen_t addrlen = sizeof addr; 36 | int sock, server; 37 | time_t t; 38 | struct tm *tm; 39 | char *timestr; 40 | 41 | server = listen_socket(argc > 1 ? atoi(argv[1]) : DEFAULT_PORT); 42 | sock = accept(server, (struct sockaddr*)&addr, &addrlen); 43 | if (sock < 0) { 44 | perror("accept(2)"); 45 | exit(1); 46 | } 47 | time(&t); 48 | tm = localtime(&t); 49 | timestr = asctime(tm); 50 | write(sock, timestr, strlen(timestr)); 51 | close(sock); 52 | close(server); 53 | exit(0); 54 | } 55 | 56 | static int 57 | listen_socket(int port) 58 | { 59 | struct addrinfo hints, *res, *ai; 60 | int err; 61 | char service[16]; 62 | 63 | memset(&hints, 0, sizeof(struct addrinfo)); 64 | /* hints.ai_family = AF_UNSPEC; */ 65 | hints.ai_family = AF_INET; 66 | hints.ai_socktype = SOCK_STREAM; 67 | hints.ai_flags = AI_PASSIVE; 68 | snprintf(service, sizeof service, "%d", port); 69 | if ((err = getaddrinfo(NULL, service, &hints, &res)) != 0) { 70 | fprintf(stderr, "%s\n", gai_strerror(err)); 71 | exit(1); 72 | } 73 | for (ai = res; ai; ai = ai->ai_next) { 74 | int sock; 75 | 76 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 77 | if (sock < 0) continue; 78 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 79 | close(sock); 80 | continue; 81 | } 82 | if (listen(sock, 5) < 0) { 83 | close(sock); 84 | continue; 85 | } 86 | freeaddrinfo(res); 87 | fprintf(stderr, "listening on port %d...\n", port); 88 | return sock; 89 | } 90 | fprintf(stderr, "cannot listen socket\n"); 91 | exit(1); 92 | } 93 | -------------------------------------------------------------------------------- /grep2.c: -------------------------------------------------------------------------------- 1 | /* 2 | grep2.c -- grep command with -i and -v options 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static void grep_file(regex_t *re, char *path); 13 | static void grep_stream(regex_t *re, FILE *f); 14 | 15 | static int opt_invert = 0; 16 | static int opt_ignorecase = 0; 17 | 18 | int 19 | main(int argc, char *argv[]) 20 | { 21 | int opt; 22 | while ((opt = getopt(argc, argv, "iv")) != -1) { 23 | switch (opt) { 24 | case 'i': 25 | opt_ignorecase = 1; 26 | break; 27 | case 'v': 28 | opt_invert = 1; 29 | break; 30 | case '?': 31 | fprintf(stderr, "Usage: %s [-iv] [...]\n", argv[0]); 32 | exit(1); 33 | } 34 | } 35 | argc -= optind; 36 | argv += optind; 37 | 38 | if (argc < 1) { 39 | fputs("no pattern\n", stderr); 40 | exit(1); 41 | } 42 | char *pattern = argv[0]; 43 | argc--; 44 | argv++; 45 | 46 | /* re는 정규표현식(Regular Expression)의 약어. 47 | regexp,regex등도 자주 사용된다 */ 48 | int re_mode = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 49 | if (opt_ignorecase) re_mode |= REG_ICASE; 50 | regex_t re; 51 | int err = regcomp(&re, pattern, re_mode); 52 | if (err != 0) { 53 | char buf[1024]; 54 | 55 | regerror(err, &re, buf, sizeof buf); 56 | puts(buf); 57 | exit(1); 58 | } 59 | if (argc == 0) { 60 | grep_stream(&re, stdin); 61 | } 62 | else { 63 | int i; 64 | for (i = 0; i < argc; i++) { 65 | grep_file(&re, argv[i]); 66 | } 67 | } 68 | regfree(&re); 69 | exit(0); 70 | } 71 | 72 | /* path의 파일에 대해 grep을 수행 */ 73 | static void 74 | grep_file(regex_t *re, char *path) 75 | { 76 | FILE *f; 77 | 78 | f = fopen(path, "r"); 79 | if (!f) { 80 | perror(path); 81 | exit(1); 82 | } 83 | grep_stream(re, f); 84 | fclose(f); 85 | } 86 | 87 | /* 88 | f로 지정한 스트림에 대해 grep을 수행. 89 | */ 90 | static void 91 | grep_stream(regex_t *re, FILE *f) 92 | { 93 | char buf[4096]; 94 | int matched; 95 | 96 | while (fgets(buf, sizeof buf, f)) { 97 | matched = (regexec(re, buf, 0, NULL, 0) == 0); 98 | if (opt_invert) { 99 | matched = !matched; 100 | } 101 | if (matched) { 102 | fputs(buf, stdout); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mkpath.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void make_path(const char *path); 9 | static void die(const char *s); 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | int i; 15 | 16 | if (argc < 2) { 17 | fprintf(stderr, "%s: no arguments\n", argv[0]); 18 | exit(1); 19 | } 20 | for (i = 1; i < argc; i++) { 21 | make_path(argv[i]); 22 | } 23 | exit(0); 24 | } 25 | 26 | /* 27 | "Check & Create" strategy may fail in multi-process environment, 28 | we try to create a directory and check an error instead. 29 | */ 30 | static void 31 | make_path(const char *path) 32 | { 33 | if (mkdir(path, 0777) == 0) { 34 | // Successfully created. OK 35 | return; 36 | } 37 | if (errno == EEXIST) { 38 | struct stat st; 39 | if (stat(path, &st) < 0) die("stat"); 40 | if (!S_ISDIR(st.st_mode)) { 41 | fprintf(stderr, "file exists but is not a directory: %s\n", path); 42 | exit(1); 43 | } 44 | // Given path is already a directory. 45 | return; 46 | } 47 | else if (errno == ENOENT) { 48 | // Parent path is not a directory. Make it 49 | char *parent_path = strdup(path); 50 | if (!parent_path) die("strdup"); 51 | 52 | // Removes duplicated trailing slashes ('/') 53 | char *last = parent_path + strlen(parent_path) - 1; 54 | while (*last == '/' && last != parent_path) { 55 | *last-- = '\0'; 56 | } 57 | 58 | if (strcmp(parent_path, "/") == 0) { 59 | fprintf(stderr, "error: root directory is not a directory???\n"); 60 | exit(1); 61 | } 62 | 63 | char *sep = strrchr(parent_path, '/'); 64 | if (!sep) { 65 | // No slash ('/') found. e.g. "component" 66 | fprintf(stderr, "error: current directory is not a directory???\n"); 67 | exit(1); 68 | } 69 | else if (sep == parent_path) { 70 | // e.g. "/component" 71 | fprintf(stderr, "error: root directory is not a directory???\n"); 72 | exit(1); 73 | } 74 | // e.g. "/a/b/c" -> "/a/b\0c" 75 | *sep = '\0'; 76 | 77 | make_path(parent_path); 78 | if (mkdir(path, 0777) < 0) die(path); 79 | return; 80 | } 81 | else { 82 | perror(path); 83 | exit(1); 84 | } 85 | } 86 | 87 | static void 88 | die(const char *s) 89 | { 90 | perror(s); 91 | exit(1); 92 | } 93 | -------------------------------------------------------------------------------- /slice2.c: -------------------------------------------------------------------------------- 1 | /* 2 | slice - Prints matched substrings (*without* line length limitation) 3 | 4 | Copyright (c) 2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static void do_slice(regex_t *pat, FILE *f); 18 | static char* get_line(FILE *f); 19 | static void die(const char *s); 20 | 21 | int 22 | main(int argc, char *argv[]) 23 | { 24 | regex_t pat; 25 | int err; 26 | int i; 27 | 28 | if (argc < 2) { 29 | fputs("no pattern\n", stderr); 30 | exit(1); 31 | } 32 | err = regcomp(&pat, argv[1], REG_EXTENDED | REG_NEWLINE); 33 | if (err != 0) { 34 | char buf[1024]; 35 | 36 | regerror(err, &pat, buf, sizeof buf); 37 | puts(buf); 38 | exit(1); 39 | } 40 | if (argc == 2) { 41 | do_slice(&pat, stdin); 42 | } 43 | else { 44 | for (i = 2; i < argc; i++) { 45 | FILE *f = fopen(argv[i], "r"); 46 | if (!f) die(argv[i]); 47 | do_slice(&pat, f); 48 | fclose(f); 49 | } 50 | } 51 | regfree(&pat); 52 | exit(0); 53 | } 54 | 55 | static void 56 | do_slice(regex_t *pat, FILE *src) 57 | { 58 | char *line; 59 | while (line = get_line(src)) { 60 | regmatch_t matched[1]; 61 | if (regexec(pat, line, 1, matched, 0) == 0) { 62 | char *str = line + matched[0].rm_so; 63 | regoff_t len = matched[0].rm_eo - matched[0].rm_so; 64 | fwrite(str, len, 1, stdout); 65 | fputc('\n', stdout); 66 | } 67 | free(line); 68 | } 69 | } 70 | 71 | static char* 72 | get_line(FILE *src) 73 | { 74 | unsigned char *line; /* バッファ */ 75 | size_t capa = 160; /* バッファサイズ */ 76 | size_t idx = 0; /* 現在のバッファ書き込み位置 */ 77 | int c; 78 | 79 | line = malloc(capa); 80 | if (!line) die("malloc"); 81 | while ((c = getc(src)) != EOF) { 82 | if (c == '\n') 83 | break; 84 | if (idx == capa - 1) { /* バッファ長チェック */ 85 | capa *= 2; 86 | line = realloc(line, capa); 87 | if (!line) die("realloc"); 88 | } 89 | line[idx++] = (unsigned char)c; 90 | } 91 | if ((idx == 0) && (c == EOF)) { 92 | free(line); 93 | return NULL; 94 | } 95 | line[idx++] = '\0'; 96 | 97 | return (char*)line; 98 | } 99 | 100 | static void 101 | die(const char *s) 102 | { 103 | perror(s); 104 | exit(1); 105 | } 106 | -------------------------------------------------------------------------------- /sh1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct cmd { 13 | char **argv; 14 | long argc; /* used length of argv */ 15 | long capa; /* allocated length of argv */ 16 | }; 17 | 18 | static void invoke_cmd(struct cmd *cmd); 19 | static struct cmd* read_cmd(void); 20 | static struct cmd* parse_cmd(char *cmdline); 21 | static void free_cmd(struct cmd *p); 22 | static void* xmalloc(size_t sz); 23 | static void* xrealloc(void *ptr, size_t sz); 24 | 25 | static char *program_name; 26 | 27 | #define PROMPT "$ " 28 | 29 | int 30 | main(int argc, char *argv[]) 31 | { 32 | program_name = argv[0]; 33 | for (;;) { 34 | struct cmd *cmd; 35 | 36 | fprintf(stdout, PROMPT); 37 | fflush(stdout); 38 | cmd = read_cmd(); 39 | if (cmd->argc > 0) { 40 | invoke_cmd(cmd); 41 | } 42 | free_cmd(cmd); 43 | } 44 | exit(0); 45 | } 46 | 47 | static void 48 | invoke_cmd(struct cmd *cmd) 49 | { 50 | pid_t pid; 51 | 52 | pid = fork(); 53 | if (pid < 0) { 54 | perror("fork"); 55 | exit(1); 56 | } 57 | if (pid > 0) { /* parent */ 58 | waitpid(pid, NULL, 0); 59 | } 60 | else { /* child */ 61 | execvp(cmd->argv[0], cmd->argv); 62 | /* error */ 63 | fprintf(stderr, "%s: command not found: %s\n", 64 | program_name, cmd->argv[0]); 65 | exit(1); 66 | } 67 | } 68 | 69 | #define LINE_BUF_SIZE 2048 70 | 71 | static struct cmd * 72 | read_cmd(void) 73 | { 74 | static char buf[LINE_BUF_SIZE]; 75 | 76 | if (fgets(buf, LINE_BUF_SIZE, stdin) == NULL) { 77 | exit(0); /* allow exit by Ctrl-D (EOF) */ 78 | } 79 | return parse_cmd(buf); 80 | } 81 | 82 | #define INIT_CAPA 16 83 | 84 | static struct cmd* 85 | parse_cmd(char *cmdline) 86 | { 87 | char *p = cmdline; 88 | struct cmd *cmd; 89 | 90 | cmd = xmalloc(sizeof(struct cmd)); 91 | cmd->argc = 0; 92 | cmd->argv = xmalloc(sizeof(char*) * INIT_CAPA); 93 | cmd->capa = INIT_CAPA; 94 | while (*p) { 95 | while (*p && isspace((int)*p)) { 96 | *p++ = '\0'; 97 | } 98 | if (*p) { 99 | if (cmd->capa <= cmd->argc + 1) { /* +1 for final NULL */ 100 | cmd->capa *= 2; 101 | cmd->argv = xrealloc(cmd->argv, cmd->capa); 102 | } 103 | cmd->argv[cmd->argc] = p; 104 | cmd->argc++; 105 | } 106 | while (*p && !isspace((int)*p)) { 107 | p++; 108 | } 109 | } 110 | cmd->argv[cmd->argc] = NULL; 111 | return cmd; 112 | } 113 | 114 | static void 115 | free_cmd(struct cmd *cmd) 116 | { 117 | free(cmd->argv); 118 | free(cmd); 119 | } 120 | 121 | static void* 122 | xmalloc(size_t size) 123 | { 124 | void *p; 125 | 126 | p = malloc(size); 127 | if (!p) { 128 | perror("malloc"); 129 | exit(1); 130 | } 131 | return p; 132 | } 133 | 134 | static void* 135 | xrealloc(void *ptr, size_t size) 136 | { 137 | void *p; 138 | 139 | if (!ptr) return xmalloc(size); 140 | p = realloc(ptr, size); 141 | if (!p) { 142 | perror("realloc"); 143 | exit(1); 144 | } 145 | return p; 146 | } 147 | -------------------------------------------------------------------------------- /traverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct strbuf { 11 | char *ptr; 12 | size_t len; 13 | }; 14 | 15 | static void traverse(struct strbuf *buf); 16 | static void traverse0(struct strbuf *buf, int first); 17 | static struct strbuf *strbuf_new(void); 18 | static void strbuf_realloc(struct strbuf *buf, size_t size); 19 | static void print_error(char *s); 20 | 21 | static char *program_name; /* used by print_error() */ 22 | 23 | int 24 | main(int argc, char *argv[]) 25 | { 26 | struct strbuf *pathbuf; 27 | 28 | if (argc != 2) { 29 | fprintf(stderr, "Usage: %s \n", argv[0]); 30 | exit(1); 31 | } 32 | program_name = argv[0]; 33 | pathbuf = strbuf_new(); 34 | strbuf_realloc(pathbuf, strlen(argv[1])); 35 | strcpy(pathbuf->ptr, argv[1]); 36 | traverse(pathbuf); 37 | exit(0); 38 | } 39 | 40 | static void 41 | traverse(struct strbuf *pathbuf) 42 | { 43 | traverse0(pathbuf, 1); 44 | } 45 | 46 | static void 47 | traverse0(struct strbuf *pathbuf, int first) 48 | { 49 | DIR *d; 50 | struct dirent *ent; 51 | struct stat st; 52 | 53 | d = opendir(pathbuf->ptr); 54 | if (!d) { 55 | switch (errno) { 56 | case ENOTDIR: /* might be replaced after last readdir(). */ 57 | return; 58 | case ENOENT: 59 | if (first) { 60 | print_error(pathbuf->ptr); 61 | exit(1); 62 | } 63 | else { 64 | /* might be removed after last readdir(). */ 65 | return; 66 | } 67 | case EACCES: /* ignore permission error. */ 68 | puts(pathbuf->ptr); 69 | print_error(pathbuf->ptr); 70 | return; 71 | default: 72 | print_error(pathbuf->ptr); 73 | exit(1); 74 | } 75 | } 76 | puts(pathbuf->ptr); 77 | while (ent = readdir(d)) { 78 | if (strcmp(ent->d_name, ".") == 0) continue; 79 | if (strcmp(ent->d_name, "..") == 0) continue; 80 | strbuf_realloc(pathbuf, pathbuf->len + 1 + strlen(ent->d_name) + 1); 81 | /* special handling for the root directory */ 82 | if (strcmp(pathbuf->ptr, "/") != 0) { 83 | strcat(pathbuf->ptr, "/"); 84 | } 85 | strcat(pathbuf->ptr, ent->d_name); 86 | if (lstat(pathbuf->ptr, &st) < 0) { 87 | switch (errno) { 88 | case ENOENT: /* might be unlinked after readdir(). */ 89 | break; 90 | case EACCES: /* ignore permission error. */ 91 | print_error(pathbuf->ptr); 92 | break; 93 | default: 94 | print_error(pathbuf->ptr); 95 | exit(1); 96 | } 97 | } 98 | else { 99 | if (S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) { 100 | traverse0(pathbuf, 0); 101 | } 102 | else { 103 | puts(pathbuf->ptr); 104 | } 105 | } 106 | *strrchr(pathbuf->ptr, '/') = '\0'; 107 | } 108 | closedir(d); 109 | } 110 | 111 | #define INITLEN 1024 112 | 113 | static struct strbuf* 114 | strbuf_new(void) 115 | { 116 | struct strbuf *buf; 117 | 118 | buf = (struct strbuf*)malloc(sizeof(struct strbuf)); 119 | if (!buf) { 120 | print_error("malloc(3)"); 121 | exit(1); 122 | } 123 | buf->ptr = malloc(INITLEN); 124 | if (!buf->ptr) { 125 | print_error("malloc(3)"); 126 | exit(1); 127 | } 128 | buf->len = INITLEN; 129 | return buf; 130 | } 131 | 132 | static void 133 | strbuf_realloc(struct strbuf *buf, size_t len) 134 | { 135 | char *tmp; 136 | 137 | if (buf->len > len) return; 138 | tmp = realloc(buf->ptr, len); 139 | if (!tmp) { 140 | print_error("realloc(3)"); 141 | exit(1); 142 | } 143 | buf->ptr = tmp; 144 | buf->len = len; 145 | } 146 | 147 | /* imitate find(1)'s error message */ 148 | static void 149 | print_error(char *s) 150 | { 151 | fprintf(stderr, "%s: %s: %s\n", program_name, s, strerror(errno)); 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 모두를 위한 리눅스 프로그래밍 4 | ![모두를 위한 리눅스 프로그래밍](http://image.kyobobook.co.kr/images/book/xlarge/408/x9791188621408.jpg) 5 | 6 | 7 | **출판사** 제이펍 8 | **원서명** ふつうのLinuxプログラミング(第2版) 9 | (원서 ISBN: 9784797386479) 10 | **저자명** 아오키 미네로 11 | **역자명** 이동규 12 | **출판일** 2018년 11월 21일 13 | **페이지** 416쪽 14 | **ISBN** 979-11-88621-40-8(93000) 15 | 16 | 17 | 18 | [### 도서 소개 페이지 바로 가기 ###](http://jpub.tistory.com/869?category=208491) 19 | 20 | 21 | ## 원서(일본어) 지원 페이지 22 | http://i.loveruby.net/stdlinux2/ 23 | 24 | ### 필요 환경 25 | 26 | 프로그램을 빌드하기 위해서는 Linux와 gcc, GNU make가 필요하다. 27 | 다음 환경에서 동작을 확인했다. 28 | 29 | - Ubuntu Linux Desktop 16.04 LTS 30 | - CentOS 7.3 31 | 32 | ### 빌드 방법 33 | 34 | 이 디렉토리 내에서 make 명령어로 프로그램을 빌드할 수 있다. 35 | 또한, make text로 테스트할 수 있다. 36 | 37 | ## 라이센스 38 | 39 | 이 디렉토리 내의 소스 코드는 전부 원저자(아오키 미네로)에게 저작권이 있으며, MIT라이센스를 따른다. 40 | 즉, 아무런 제한 없이 변경, 재배포, 재사용이 가능하다. 41 | 그러나 이 소스 코드를 활용함으로써 발생하는 문제에 대해서는 저자 및 출판사는 책임지지 않는다. 42 | 43 | 아오키 미네로(青木峰郎) / Minero Aoki 44 | 45 | ## 소스 코드 목록 46 | 47 | ### 1장 48 |
49 |
hello.c
50 |
Hello, World!
51 | 52 |
args.c
53 |
실행 인자를 표시하는 프로그램.
54 | 55 |
segv.c
56 |
NULL 포인터를 참조하는 프로그램. 57 | 확실히 segmentation fault가 발생함.
58 | 59 |
echo.c
60 |
간단한 echo 명령어.
61 |
62 | 63 | ### 4장 64 |
65 |
bell.c
66 |
단말에 벨을 울리는 프로그램.
67 |
68 | 69 | ### 5장 70 |
71 |
cat.c
72 |
간단한 cat 프로그램(시스템 콜 사용 구현).
73 | 74 |
cat0.c
75 |
cat.c에서 에러 처리를 제거한 코드.
76 | 77 |
cat3.c
78 |
문제 5-1의 해답. 79 | 실행 인자가 없을 때 stdin에서 읽는 cat 명령어.
80 | 81 |
wc-l-syscall.c
82 |
문제 5-2의 해답. 83 | 표준 입력에서 텍스트를 읽어, 그 줄 수를 표시.
84 |
85 | 86 | ### 6장 87 |
88 |
cat2.c
89 |
stdio을 사용하여 구현한 cat 명령어.
90 | 91 |
getcperf.c
92 |
fgetc와 getc의 속도를 비교하기 위한 코드. 93 | 인자를 지정하지 않으면, getc를 쓰고, 인자로 f를 지정하면 fgetc를 사용한다.
94 | 95 |
feof-bug.c
96 |
feof를 사용할 때 발생하는 유명한 버그를 확인하는 코드. 97 | 이 프로그램에 표준 입력으로 텍스트 파일을 리다이렉트하면 마지막 줄이 두 번 표시된다.
98 | 99 |
cat-escape.c
100 |
문제 6-1의 해답. 101 | 탭을 \t, 개행을 $+ 개행으로 표시하는 cat 명령어.
102 | 103 |
wc-l-stdio.c
104 |
문제 6-2의 해답. 105 | 표준 입력에서 텍스트를 읽어, 그 줄 수를 표시.
106 | 107 |
cat5.c
108 |
문제 6-3의 해답. 109 | fread와 fwrite를 사용한 cat 명령어.
110 |
111 | 112 | ### 7장 113 |
114 |
head.c
115 |
간단한 head 명령어. 116 | stdin만을 읽는다.
117 | 118 |
head2.c
119 |
간단한 head 명령어. 120 | 실행 인자로 지정한 파일도 읽는다.
121 | 122 |
head3.c
123 |
간단한 head 명령어. 124 | getopt로 실행 인자를 옵션으로 파싱하는 예
125 | 126 |
head4.c
127 |
간단한 head 명령어. 128 | getopt_long을 사용하여 실행 인자를 옵션으로 파싱하는 예.
129 | 130 |
gdb/*
131 |
gdb 테스트를 위해 사용한 파일.
132 | 133 |
cat4.c
134 |
문제 7-1의 해답. 135 | -e 옵션으로 cat-escape.c와 같은 효과를 내는 cat 명령어.
136 | 137 |
tail.c
138 |
문제 7-2의 해답. 139 | 간단한 tail 명령어(출력 줄 수 고정).
140 |
141 | 142 | ### 8장 143 |
144 |
grep.c
145 |
8장에서 작성한 grep 명령어.
146 | 147 |
array.c
148 |
문자열과 문자의 배열이 같은 것을 확인하는 코드.
149 | 150 |
wgrep.c
151 |
wchar을 사용하여 멀티 바이트 문자를 지원하는 grep 명령어. 152 | 고정 문자열만 검색할 수 있다.
153 | 154 |
grep2.c
155 |
문제 8-1의 해답. 156 | -i 옵션과 -v 옵션이 붙은 grep 명령어.
157 | 158 |
slice.c
159 |
문제 8-2의 해답. 160 | 패턴에 일치하는 부분만 출력한다. 161 |
162 |
163 | 164 | ### 10장 165 |
166 |
ls.c
167 |
간단한 ls 명령어.
168 | 169 |
catdir.c
170 |
opendir를 사용하지 않고 디렉터리를 읽는 명령어. 171 | ./catdir . | strings와 같이 실행하면 ls와 비슷하게 표시함.
172 | 173 |
mkdir.c
174 |
간단한 mkdir 명령어. 175 | mkdir(2) 사용 예.
176 | 177 |
rmdir.c
178 |
간단한 rmdir 명령어. 179 | rmdir(2) 사용 예.
180 | 181 |
ln.c
182 |
간단한 ln 명령어. 183 | link(2) 사용 예.
184 | 185 |
symlink.c
186 |
간단한 ln -s 명령어. 187 | symlink(2) 사용 예.
188 | 189 |
rm.c
190 |
간단한 rm 명령어. 191 | unlink(2) 사용 예.
192 | 193 |
mv.c
194 |
간단한 mv 명령어. 195 | rename(2) 사용 예.
196 | 197 |
stat.c
198 |
실행 인자로 지정한 파일의 메타 정보를 출력. 199 | lstat(2) 사용 예.
200 | 201 |
touch.c
202 |
간단한 touch 명령어. 203 | utime(2) 사용 예. 204 | 이 명령어는 시간을 갱신할 뿐, 파일을 만드는 기능은 없음.
205 | 206 |
chmod.c
207 |
간단한 chmod 명령어. 208 | chmod(2) 사용 예.
209 | 210 |
chown.c
211 |
간단한 chown 명령어. 212 | chown(2)와 getpwnam(3) 사용 예.
213 | 214 |
chgrp.c
215 |
간단한 chgrp 명령어. 216 | chgrp(2) 사용 예.
217 | 218 |
traverse.c
219 |
문제 10-1의 해답. 220 | 실행 인자로 지정한 디렉토리를 traverse하면서, 안에 있는 파일을 출력. 221 |
222 | 223 |
mkpath.c
224 |
문제 10-3의 해답. 225 | 디렉토리 트리를 재귀적으로 작성. 226 |
227 |
228 | 229 | ### 11장 230 |
231 |
mapwrite.c
232 |
mmap(2) 테스트 프로그램.
233 | 234 |
shlib/*
235 |
공유 라이브러리를 작성하는 예(리눅스 only).
236 | 237 |
link/*
238 |
라이브러리의 다이나믹 링크와 다이나믹 로드의 예(리눅스 only).
239 | 240 |
tail.c
241 |
문제 11-1의 해답. 242 | 간단한 tail 명령어(출력 줄수를 실행 인자로 지정 가능).
243 |
244 | 245 | ### 12장 246 |
247 |
exec.c
248 |
exec 테스트 코드.
249 | 250 |
spawn.c
251 |
실행 인자로 전달된 명령어를 실행.
252 | 253 |
dupread.c
254 |
dup(2) 테스트 프로그램.
255 | 256 |
sh1.c
257 |
문제 12-2의 해답. 258 | 간단한 셸.
259 | 260 |
sh2.c
261 |
문제 12-3의 해답. 262 | 파이프와 리다이렉트 기능을 구현한 셸.
263 |
264 | 265 | ### 13장 266 |
267 |
sig.c
268 |
signal(2) 테스트 프로그램. SIGINT만 지원.
269 | 270 |
sigqueue-test.c
271 |
시그널이 큐잉되는지 테스트하는 프로그램. 272 | 리눅스에서는 프로그램이 기동된 후 재빨리 Ctrl+C를 다섯 번 입력해도(즉, 시그널을 다섯 번 전송해도), 메시지는 두 번만 표시된다. 나머지 메시지는 분실된 것이다.
273 | 274 |
isatty.c
275 |
stdin, stdout, stderr에 대해 단말인지 여부 판정.
276 | 277 |
trap.c
278 |
문제 13-1 의 해답. 279 | SIGINT(Ctrl+C로 인한 인터럽트)를 받으면 메시지를 출력.
280 |
281 | 282 | ### 14장 283 |
284 |
pwd.c
285 |
간단한 pwd 명령어. 286 | 버퍼의 크기는 정수 사용.
287 | 288 |
pwd2.c
289 |
간단한 pwd 명령어. 290 | 스스로 버퍼의 길이를 조절.
291 | 292 |
pwd3.c
293 |
간단한 pwd 명령어. 294 | GNU libc의 getcwd() 기능 사용(리눅스 Only).
295 | 296 |
showenv.c
297 |
getenv(3) 테스트 프로그램. 298 | 실행 인자로 지정된 환경 변수의 값을 표시. 299 | env.c도 참고할 것.
300 | 301 |
env.c
302 |
간단한 env 명령어. 303 | 모든 환경 변수를 표시.
304 | 305 |
user.c
306 |
실행 인자로 지정된 사용자의 사용자 ID를 표시. 307 | getpwnam(3) 사용 예.
308 | 309 |
id.c
310 |
간단한 id 명령어. 311 | user.c도 참고할 것.
312 | 313 |
timefmt.c
314 |
시간 API 테스트 프로그램.
315 | 316 |
strftime.c
317 |
strftime(3) 테스트 프로그램.
318 | 319 |
ls2.c
320 |
문제 14-3의 해답. 321 | 간단한 ls 명령어. 322 | 파일 소유자와 최종 변경 시간을 표시.
323 |
324 | 325 | ### 15장 326 |
327 |
daytime.c
328 |
daytime 클라이언트.
329 | 330 |
daytimed.c
331 |
daytime 서버. 332 | 책에 기술한 이유로 IPv4 전용.
333 |
334 | 335 | ### 16장 336 |
337 |
httpd.c
338 |
16장에서 만든 HTTP 서버. 339 | stdin에서만 요청을 읽어 들인다. 340 |
341 |
342 | 343 | ### 17장 344 |
345 |
httpd2.c
346 |
이번 장에서 작성한 HTTP 서버. 347 | 데몬화 기능과 소켓 접속 기능 추가.
348 | 349 |
logging.c
350 |
syslog(3) 테스트 프로그램.
351 |
352 | 353 | ### 18장 354 |
355 |
make/*
356 |
make와 Makefile의 예.
357 |
358 | -------------------------------------------------------------------------------- /sh2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct cmd { 13 | int argc; 14 | char **argv; 15 | int capa; 16 | int status; 17 | int pid; 18 | struct cmd *next; 19 | }; 20 | 21 | #define REDIRECT_P(cmd) ((cmd)->argc == -1) 22 | #define PID_BUILTIN -2 23 | #define BUILTIN_P(cmd) ((cmd)->pid == PID_BUILTIN) 24 | 25 | struct builtin { 26 | char *name; 27 | int (*f)(int argc, char *argv[]); 28 | }; 29 | 30 | static void prompt(void); 31 | static int invoke_commands(struct cmd *cmd); 32 | static void exec_pipeline(struct cmd *cmdhead); 33 | static void redirect_stdout(char *path); 34 | static int wait_pipeline(struct cmd *cmdhead); 35 | static struct cmd* pipeline_tail(struct cmd *cmdhead); 36 | static struct cmd* parse_command_line(char *cmdline); 37 | static void free_cmd(struct cmd *p); 38 | static struct builtin* lookup_builtin(char *name); 39 | static int builtin_cd(int argc, char *argv[]); 40 | static int builtin_pwd(int argc, char *argv[]); 41 | static int builtin_exit(int argc, char *argv[]); 42 | static void* xmalloc(size_t sz); 43 | static void* xrealloc(void *ptr, size_t sz); 44 | 45 | static char *program_name; 46 | 47 | int 48 | main(int argc, char *argv[]) 49 | { 50 | program_name = argv[0]; 51 | for (;;) { 52 | prompt(); 53 | } 54 | exit(0); 55 | } 56 | 57 | #define LINEBUF_MAX 2048 58 | 59 | static void 60 | prompt(void) 61 | { 62 | static char buf[LINEBUF_MAX]; 63 | struct cmd *cmd; 64 | 65 | fprintf(stdout, "$ "); 66 | fflush(stdout); 67 | if (fgets(buf, LINEBUF_MAX, stdin) == NULL) 68 | exit(0); 69 | cmd = parse_command_line(buf); 70 | if (cmd == NULL) { 71 | fprintf(stderr, "%s: syntax error\n", program_name); 72 | return; 73 | } 74 | if (cmd->argc > 0) 75 | invoke_commands(cmd); 76 | free_cmd(cmd); 77 | } 78 | 79 | static int 80 | invoke_commands(struct cmd *cmdhead) 81 | { 82 | int st; 83 | int original_stdin = dup(0); 84 | int original_stdout = dup(1); 85 | 86 | exec_pipeline(cmdhead); 87 | st = wait_pipeline(cmdhead); 88 | close(0); dup2(original_stdin, 0); close(original_stdin); 89 | close(1); dup2(original_stdout, 1); close(original_stdout); 90 | 91 | return st; 92 | } 93 | 94 | #define HEAD_P(cmd) ((cmd) == cmdhead) 95 | #define TAIL_P(cmd) (((cmd)->next == NULL) || REDIRECT_P((cmd)->next)) 96 | 97 | static void 98 | exec_pipeline(struct cmd *cmdhead) 99 | { 100 | struct cmd *cmd; 101 | int fds1[2] = {-1, -1}; 102 | int fds2[2] = {-1, -1}; 103 | 104 | for (cmd = cmdhead; cmd && !REDIRECT_P(cmd); cmd = cmd->next) { 105 | fds1[0] = fds2[0]; 106 | fds1[1] = fds2[1]; 107 | if (! TAIL_P(cmd)) { 108 | if (pipe(fds2) < 0) { 109 | perror("pipe"); 110 | exit(3); 111 | } 112 | } 113 | if (lookup_builtin(cmd->argv[0]) != NULL) { 114 | cmd->pid = PID_BUILTIN; 115 | } 116 | else { 117 | cmd->pid = fork(); 118 | if (cmd->pid < 0) { 119 | perror("fork"); 120 | exit(3); 121 | } 122 | if (cmd->pid > 0) { /* parent */ 123 | if (fds1[0] != -1) close(fds1[0]); 124 | if (fds1[1] != -1) close(fds1[1]); 125 | continue; 126 | } 127 | } 128 | if (! HEAD_P(cmd)) { 129 | close(0); dup2(fds1[0], 0); close(fds1[0]); 130 | close(fds1[1]); 131 | } 132 | if (! TAIL_P(cmd)) { 133 | close(fds2[0]); 134 | close(1); dup2(fds2[1], 1); close(fds2[1]); 135 | } 136 | if ((cmd->next != NULL) && REDIRECT_P(cmd->next)) { 137 | redirect_stdout(cmd->next->argv[0]); 138 | } 139 | if (!BUILTIN_P(cmd)) { 140 | execvp(cmd->argv[0], cmd->argv); 141 | fprintf(stderr, "%s: command not found: %s\n", 142 | program_name, cmd->argv[0]); 143 | exit(1); 144 | } 145 | } 146 | } 147 | 148 | static void 149 | redirect_stdout(char *path) 150 | { 151 | int fd; 152 | 153 | close(1); 154 | fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0666); 155 | if (fd < 0) { 156 | perror(path); 157 | return; 158 | } 159 | if (fd != 1) { 160 | dup2(fd, 1); 161 | close(fd); 162 | } 163 | } 164 | 165 | static int 166 | wait_pipeline(struct cmd *cmdhead) 167 | { 168 | struct cmd *cmd; 169 | 170 | for (cmd = cmdhead; cmd && !REDIRECT_P(cmd); cmd = cmd->next) { 171 | if (BUILTIN_P(cmd)) 172 | cmd->status = lookup_builtin(cmd->argv[0])->f(cmd->argc, cmd->argv); 173 | else 174 | waitpid(cmd->pid, &cmd->status, 0); 175 | } 176 | return pipeline_tail(cmdhead)->status; 177 | } 178 | 179 | static struct cmd* 180 | pipeline_tail(struct cmd *cmdhead) 181 | { 182 | struct cmd *cmd; 183 | 184 | for (cmd = cmdhead; !TAIL_P(cmd); cmd = cmd->next) 185 | ; 186 | return cmd; 187 | } 188 | 189 | #define INIT_ARGV 8 190 | #define IDENT_CHAR_P(c) (!isspace((int)c) && ((c) != '|') && ((c) != '>')) 191 | 192 | static struct cmd* 193 | parse_command_line(char *p) 194 | { 195 | struct cmd *cmd; 196 | 197 | cmd = xmalloc(sizeof(struct cmd)); 198 | cmd->argc = 0; 199 | cmd->argv = xmalloc(sizeof(char*) * INIT_ARGV); 200 | cmd->capa = INIT_ARGV; 201 | cmd->next = NULL; 202 | while (*p) { 203 | while (*p && isspace((int)*p)) 204 | *p++ = '\0'; 205 | if (! IDENT_CHAR_P(*p)) 206 | break; 207 | if (*p && IDENT_CHAR_P(*p)) { 208 | if (cmd->capa <= cmd->argc) { 209 | cmd->capa *= 2; 210 | cmd->argv = xrealloc(cmd->argv, cmd->capa); 211 | } 212 | cmd->argv[cmd->argc] = p; 213 | cmd->argc++; 214 | } 215 | while (*p && IDENT_CHAR_P(*p)) 216 | p++; 217 | } 218 | if (cmd->capa <= cmd->argc) { 219 | cmd->capa += 1; 220 | cmd->argv = xrealloc(cmd->argv, cmd->capa); 221 | } 222 | cmd->argv[cmd->argc] = NULL; 223 | 224 | if (*p == '|' || *p == '>') { 225 | if (cmd == NULL || cmd->argc == 0) goto parse_error; 226 | cmd->next = parse_command_line(p + 1); 227 | if (cmd->next == NULL || cmd->next->argc == 0) goto parse_error; 228 | if (*p == '>') { 229 | if (cmd->next->argc != 1) goto parse_error; 230 | cmd->next->argc = -1; 231 | } 232 | *p = '\0'; 233 | } 234 | 235 | return cmd; 236 | 237 | parse_error: 238 | if (cmd) free_cmd(cmd); 239 | return NULL; 240 | } 241 | 242 | static void 243 | free_cmd(struct cmd *cmd) 244 | { 245 | if (cmd->next != NULL) 246 | free_cmd(cmd->next); 247 | free(cmd->argv); 248 | free(cmd); 249 | } 250 | 251 | struct builtin builtins_list[] = { 252 | {"cd", builtin_cd}, 253 | {"pwd", builtin_pwd}, 254 | {"exit", builtin_exit}, 255 | {NULL, NULL} 256 | }; 257 | 258 | static struct builtin* 259 | lookup_builtin(char *cmd) 260 | { 261 | struct builtin *p; 262 | 263 | for (p = builtins_list; p->name; p++) { 264 | if (strcmp(cmd, p->name) == 0) 265 | return p; 266 | } 267 | return NULL; 268 | } 269 | 270 | static int 271 | builtin_cd(int argc, char *argv[]) 272 | { 273 | if (argc != 2) { 274 | fprintf(stderr, "%s: wrong argument\n", argv[0]); 275 | return 1; 276 | } 277 | if (chdir(argv[1]) < 0) { 278 | perror(argv[1]); 279 | return 1; 280 | } 281 | return 0; 282 | } 283 | 284 | static int 285 | builtin_pwd(int argc, char *argv[]) 286 | { 287 | char buf[PATH_MAX]; 288 | 289 | if (argc != 1) { 290 | fprintf(stderr, "%s: wrong argument\n", argv[0]); 291 | return 1; 292 | } 293 | if (!getcwd(buf, PATH_MAX)) { 294 | fprintf(stderr, "%s: cannot get working directory\n", argv[0]); 295 | return 1; 296 | } 297 | printf("%s\n", buf); 298 | return 0; 299 | } 300 | 301 | static int 302 | builtin_exit(int argc, char *argv[]) 303 | { 304 | if (argc != 1) { 305 | fprintf(stderr, "%s: too many arguments\n", argv[0]); 306 | return 1; 307 | } 308 | exit(0); 309 | } 310 | 311 | static void* 312 | xmalloc(size_t sz) 313 | { 314 | void *p; 315 | 316 | p = calloc(1, sz); 317 | if (!p) 318 | exit(3); 319 | return p; 320 | } 321 | 322 | static void* 323 | xrealloc(void *ptr, size_t sz) 324 | { 325 | void *p; 326 | 327 | if (!ptr) return xmalloc(sz); 328 | p = realloc(ptr, sz); 329 | if (!p) 330 | exit(3); 331 | return p; 332 | } 333 | -------------------------------------------------------------------------------- /test-scripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | n_tests=0 4 | failed=0 5 | test_finished() { 6 | rm -rf tc.* 7 | echo 8 | if [ "$failed" = "0" ] 9 | then 10 | echo "PASS ($n_tests tests) -- `hostname` / `uname -srm`" 11 | exit 0 12 | else 13 | echo "FAIL ($failed/$n_tests failed) -- `hostname` / `uname -srm`" 14 | exit 1 15 | fi 16 | } 17 | 18 | print() { 19 | if [ `/bin/echo -n x` = "-n" ] 20 | then 21 | /bin/echo $1'\c' 22 | else 23 | /bin/echo -n $1 24 | fi 25 | } 26 | 27 | progress() { 28 | print '.' 29 | } 30 | 31 | begin_test() { 32 | progress 33 | n_tests=`expr $n_tests "+" 1` 34 | } 35 | 36 | test_failed() { 37 | failed=`expr $failed "+" 1` 38 | } 39 | 40 | assert_equal() { 41 | begin_test 42 | _f="no" 43 | excmd=$1; shift 44 | mycmd=$1; shift 45 | eval "$excmd" >tc.out.expected 2>tc.err.expected 46 | eval "$mycmd" >tc.out.real 2>tc.err.real 47 | assert_not_coredump || return 48 | cmp tc.out.real tc.out.expected >/dev/null 2>&1 49 | if [ "$?" != "0" ] 50 | then 51 | echo "stdout differ: \"$excmd\" and \"$mycmd\"" 52 | diff -u tc.out.expected tc.out.real 53 | echo "----" 54 | _f="yes" 55 | fi 56 | cmp tc.err.real tc.err.expected >/dev/null 2>&1 57 | if [ "$?" != "0" ] 58 | then 59 | echo "stderr differ: \"$excmd\" and \"$mycmd\"" 60 | diff -u tc.err.expected tc.err.real 61 | echo "----" 62 | _f="yes" 63 | fi 64 | [ "$_f" = "yes" ] && test_failed 65 | } 66 | 67 | assert_equal_stdout() { 68 | begin_test 69 | _f="no" 70 | excmd=$1; shift 71 | mycmd=$1; shift 72 | eval "$excmd" >tc.out.expected 2>/dev/null 73 | eval "$mycmd" >tc.out.real 2>/dev/null 74 | assert_not_coredump || return 75 | cmp tc.out.real tc.out.expected >/dev/null 2>&1 76 | if [ "$?" != "0" ] 77 | then 78 | echo "stdout differ: \"$excmd\" and \"$mycmd\"" 79 | diff -u tc.out.expected tc.out.real 80 | echo "----" 81 | test_failed 82 | fi 83 | } 84 | 85 | assert_stdout() { 86 | begin_test 87 | expected=$1; shift 88 | mycmd=$1; shift 89 | echo "$expected" > tc.out.expected 90 | eval "$mycmd" >tc.out.real 2>/dev/null 91 | assert_not_coredump || return 92 | cmp tc.out.expected tc.out.real >/dev/null 2>&1 93 | if [ "$?" != "0" ] 94 | then 95 | echo "stdout differ: string \"$expected\" and cmd \"$mycmd\"" 96 | diff -u tc.out.expected tc.out.real 97 | echo "----" 98 | test_failed 99 | fi 100 | } 101 | 102 | rm -f core 103 | assert_not_coredump() { 104 | begin_test 105 | if [ -f core ] 106 | then 107 | echo "core dumped: $mycmd" 108 | echo "----" 109 | test_failed 110 | return 1 111 | fi 112 | rm -f core 113 | return 0 114 | } 115 | 116 | assert_not_exist() { 117 | begin_test 118 | file=$1; shift 119 | if [ -f $file ] 120 | then 121 | echo "exists: $file" 122 | echo "----" 123 | test_failed 124 | return 1 125 | fi 126 | } 127 | 128 | assert_directory() { 129 | begin_test 130 | dir=$1; shift 131 | if [ ! -d $dir ] 132 | then 133 | echo "not directory: $dir" 134 | echo "----" 135 | test_failed 136 | return 1 137 | fi 138 | 139 | } 140 | 141 | Head() { 142 | if [ `uname -s` = "SunOS" ] 143 | then 144 | : 145 | else 146 | head ${@+"$@"} 147 | fi 148 | } 149 | 150 | uidof() { 151 | id $1 | sed -e 's/=/ /' -e 's/(/ /' | awk '{print $2}' 152 | } 153 | 154 | assert_stdout "argc=1 155 | argv[0]=./args" './args' 156 | 157 | assert_stdout "argc=2 158 | argv[0]=./args 159 | argv[1]=x" './args x' 160 | 161 | assert_stdout "argc=3 162 | argv[0]=./args 163 | argv[1]=x 164 | argv[2]=y" './args x y' 165 | 166 | assert_stdout "Hello, World!" './hello' 167 | 168 | assert_stdout "" './echo' 169 | assert_stdout "a" './echo a' 170 | assert_stdout "a b c" './echo a b c' 171 | assert_stdout "a b c" './echo a "b c"' 172 | assert_stdout "a b c" './echo a "b c"' 173 | 174 | assert_equal 'cat cat.c' './cat cat.c' 175 | assert_equal 'cat httpd2.c' './cat httpd2.c' 176 | assert_equal 'cat cat.c' './cat2 cat.c' 177 | assert_equal 'cat httpd2.c' './cat2 httpd2.c' 178 | assert_equal 'cat cat.c' './cat3 cat.c' 179 | assert_equal 'cat httpd2.c' './cat3 httpd2.c' 180 | assert_equal 'cat cat.c' './cat3 < cat.c' 181 | assert_equal 'cat httpd2.c' './cat3 < httpd2.c' 182 | assert_equal 'cat data.esc' './cat-escape data.esc0' 183 | assert_equal 'cat cat.c' './cat4 cat.c' 184 | assert_equal 'cat httpd2.c' './cat4 httpd2.c' 185 | assert_equal 'cat cat.c' './cat4 < cat.c' 186 | assert_equal 'cat httpd2.c' './cat4 < httpd2.c' 187 | assert_equal 'cat data.esc' './cat4 -e data.esc0' 188 | assert_equal 'cat data.esc' './cat4 -e < data.esc0' 189 | 190 | assert_equal 'Head -n 0 head.c' './head 0 < head.c' 191 | assert_equal 'head -n 5 head.c' './head 5 < head.c' 192 | assert_equal 'head -n 999 head.c' './head 999 < head.c' 193 | assert_equal 'Head -n 0 head.c' './head2 0 < head.c' 194 | assert_equal 'head -n 5 head.c' './head2 5 < head.c' 195 | assert_equal 'head -n 999 head.c' './head2 999 < head.c' 196 | assert_equal 'Head -n 0 head.c' './head3 -n 0 < head.c' 197 | assert_equal 'head -n 5 head.c' './head3 -n 5 < head.c' 198 | assert_equal 'head -n 999 head.c' './head3 -n 999 < head.c' 199 | if [ -f head4 ]; then 200 | assert_equal 'Head -n 0 head.c' './head4 -n 0 < head.c' 201 | assert_equal 'head -n 5 head.c' './head4 -n 5 < head.c' 202 | assert_equal 'head -n 999 head.c' './head4 -n 999 < head.c' 203 | assert_equal 'Head -n 0 head.c' './head4 --lines=0 < head.c' 204 | assert_equal 'head -n 5 head.c' './head4 --lines=5 < head.c' 205 | assert_equal 'head -n 999 head.c' './head4 --lines=999 < head.c' 206 | fi 207 | 208 | assert_equal 'tail -0 tail.c' './tail2 0 < tail.c' 209 | assert_equal 'tail -5 tail.c' './tail2 5 < tail.c' 210 | assert_equal 'tail -999 tail.c' './tail2 999 < tail.c' 211 | 212 | assert_equal 'grep close head.c' './grep close head.c' 213 | assert_equal 'grep NOTMATCH head.c' './grep NOTMATCH head.c' 214 | assert_equal 'grep close /dev/null' './grep close /dev/null' 215 | assert_equal 'grep close head.c' './grep close head.c' 216 | assert_equal 'grep "open|close" head.c' './grep "open|close" head.c' 217 | 218 | assert_equal 'grep close head.c' './grep2 close head.c' 219 | assert_equal 'grep NOTMATCH head.c' './grep2 NOTMATCH head.c' 220 | assert_equal 'grep close /dev/null' './grep2 close /dev/null' 221 | assert_equal 'grep close head.c' './grep2 close head.c' 222 | assert_equal 'grep "open|close" head.c' './grep2 "open|close" head.c' 223 | assert_equal 'grep -v close head.c' './grep2 -v close head.c' 224 | assert_equal 'grep -v NOTMATCH head.c' './grep2 -v NOTMATCH head.c' 225 | assert_equal 'grep CLOSE head.c' './grep2 CLOSE head.c' 226 | assert_equal 'grep -i CLOSE head.c' './grep2 -i CLOSE head.c' 227 | assert_equal './grep2 -i close head.c' './grep2 -i cLoSe head.c' 228 | 229 | assert_equal 'grep close head.c' './grep3 close head.c' 230 | assert_equal 'grep NOTMATCH head.c' './grep3 NOTMATCH head.c' 231 | assert_equal 'grep close /dev/null' './grep3 close /dev/null' 232 | assert_equal 'grep close head.c' './grep3 close head.c' 233 | assert_equal 'grep "open|close" head.c' './grep3 "open|close" head.c' 234 | 235 | # FIXME: ln 236 | # FIXME: symlink 237 | 238 | touch tc.tmp 239 | ./rm tc.tmp 240 | assert_not_exist tc.tmp 241 | touch tc.tmp1 tc.tmp2 tc.tmp3 242 | ./rm tc.tmp* 243 | assert_not_exist tc.tmp1 244 | assert_not_exist tc.tmp2 245 | assert_not_exist tc.tmp3 246 | rm -f tc.tmp* 247 | 248 | mkdir -p tc.tmpdir 249 | ./rmdir tc.tmpdir 250 | assert_not_exist tc.tmpdir 251 | mkdir -p tc.tmpdir1 tc.tmpdir2 tc.tmpdir3 252 | ./rmdir tc.tmpdir1 tc.tmpdir2 tc.tmpdir3 253 | assert_not_exist tc.tmpdir1 254 | assert_not_exist tc.tmpdir2 255 | assert_not_exist tc.tmpdir3 256 | rm -rf tc.tmpdir* 257 | 258 | rm -rf tc.tmpdir 259 | ./mkdir tc.tmpdir 260 | assert_not_coredump 261 | assert_directory tc.tmpdir 262 | rm -rf tc.tmpdir* 263 | ./mkdir tc.tmpdir1 tc.tmpdir2 tc.tmpdir3 264 | assert_not_coredump 265 | assert_directory tc.tmpdir1 266 | assert_directory tc.tmpdir2 267 | assert_directory tc.tmpdir3 268 | rm -rf tc.tmpdir* 269 | 270 | assert_equal 'ls -a' './ls . | sort' 271 | assert_equal 'ls -a /' './ls / | sort' 272 | 273 | assert_equal 'env | grep -v "^_"' './env | grep -v "^_"' 274 | assert_equal '/bin/pwd' './pwd' 275 | assert_equal '/bin/pwd' './pwd2' 276 | if [ -f pwd3 ]; then 277 | assert_equal '/bin/pwd' './pwd3' 278 | fi 279 | 280 | assert_stdout "id=`uidof root`" './user root' 281 | assert_stdout "id=`uidof nobody`" './user nobody' 282 | 283 | assert_equal "id | awk '{print \$1}'" "./id | awk '{print \$1}'" 284 | assert_equal "id | awk '{print \$2}'" "./id | awk '{print \$2}'" 285 | 286 | assert_equal_stdout 'find .' './traverse .' 287 | 288 | print '' > tc.tmp 289 | assert_stdout "0" './wc-l-stdio tc.tmp' 290 | print 'a' > tc.tmp 291 | assert_stdout "1" './wc-l-stdio tc.tmp' 292 | echo 'a' > tc.tmp 293 | assert_stdout "1" './wc-l-stdio tc.tmp' 294 | echo 'a' > tc.tmp 295 | print 'b' >> tc.tmp 296 | assert_stdout "2" './wc-l-stdio tc.tmp' 297 | echo 'a' > tc.tmp 298 | echo 'b' >> tc.tmp 299 | assert_stdout "2" './wc-l-stdio tc.tmp' 300 | rm -f tc.tmp 301 | 302 | test_finished 303 | -------------------------------------------------------------------------------- /httpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | httpd.c 3 | 4 | Copyright (c) 2004,2005,2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /****** Constants ********************************************************/ 26 | 27 | #define SERVER_NAME "LittleHTTP" 28 | #define SERVER_VERSION "1.0" 29 | #define HTTP_MINOR_VERSION 0 30 | #define BLOCK_BUF_SIZE 1024 31 | #define LINE_BUF_SIZE 4096 32 | #define MAX_REQUEST_BODY_LENGTH (1024 * 1024) 33 | 34 | /****** Data Type Definitions ********************************************/ 35 | 36 | struct HTTPHeaderField { 37 | char *name; 38 | char *value; 39 | struct HTTPHeaderField *next; 40 | }; 41 | 42 | struct HTTPRequest { 43 | int protocol_minor_version; 44 | char *method; 45 | char *path; 46 | struct HTTPHeaderField *header; 47 | char *body; 48 | long length; 49 | }; 50 | 51 | struct FileInfo { 52 | char *path; 53 | long size; 54 | int ok; 55 | }; 56 | 57 | /****** Function Prototypes **********************************************/ 58 | 59 | typedef void (*sighandler_t)(int); 60 | static void install_signal_handlers(void); 61 | static void trap_signal(int sig, sighandler_t handler); 62 | static void signal_exit(int sig); 63 | static void service(FILE *in, FILE *out, char *docroot); 64 | static struct HTTPRequest* read_request(FILE *in); 65 | static void read_request_line(struct HTTPRequest *req, FILE *in); 66 | static struct HTTPHeaderField* read_header_field(FILE *in); 67 | static void upcase(char *str); 68 | static void free_request(struct HTTPRequest *req); 69 | static long content_length(struct HTTPRequest *req); 70 | static char* lookup_header_field_value(struct HTTPRequest *req, char *name); 71 | static void respond_to(struct HTTPRequest *req, FILE *out, char *docroot); 72 | static void do_file_response(struct HTTPRequest *req, FILE *out, char *docroot); 73 | static void method_not_allowed(struct HTTPRequest *req, FILE *out); 74 | static void not_implemented(struct HTTPRequest *req, FILE *out); 75 | static void not_found(struct HTTPRequest *req, FILE *out); 76 | static void output_common_header_fields(struct HTTPRequest *req, FILE *out, char *status); 77 | static struct FileInfo* get_fileinfo(char *docroot, char *path); 78 | static char* build_fspath(char *docroot, char *path); 79 | static void free_fileinfo(struct FileInfo *info); 80 | static char* guess_content_type(struct FileInfo *info); 81 | static void* xmalloc(size_t sz); 82 | static void log_exit(char *fmt, ...); 83 | 84 | /****** Functions ********************************************************/ 85 | 86 | int 87 | main(int argc, char *argv[]) 88 | { 89 | if (argc != 2) { 90 | fprintf(stderr, "Usage: %s \n", argv[0]); 91 | exit(1); 92 | } 93 | install_signal_handlers(); 94 | service(stdin, stdout, argv[1]); 95 | exit(0); 96 | } 97 | 98 | static void 99 | install_signal_handlers(void) 100 | { 101 | trap_signal(SIGPIPE, signal_exit); 102 | } 103 | 104 | static void 105 | trap_signal(int sig, sighandler_t handler) 106 | { 107 | struct sigaction act; 108 | 109 | act.sa_handler = handler; 110 | sigemptyset(&act.sa_mask); 111 | act.sa_flags = SA_RESTART; 112 | if (sigaction(sig, &act, NULL) < 0) 113 | log_exit("sigaction() failed: %s", strerror(errno)); 114 | } 115 | 116 | static void 117 | signal_exit(int sig) 118 | { 119 | log_exit("exit by signal %d", sig); 120 | } 121 | 122 | static void 123 | service(FILE *in, FILE *out, char *docroot) 124 | { 125 | struct HTTPRequest *req; 126 | 127 | req = read_request(in); 128 | respond_to(req, out, docroot); 129 | free_request(req); 130 | } 131 | 132 | static struct HTTPRequest* 133 | read_request(FILE *in) 134 | { 135 | struct HTTPRequest *req; 136 | struct HTTPHeaderField *h; 137 | 138 | req = xmalloc(sizeof(struct HTTPRequest)); 139 | read_request_line(req, in); 140 | req->header = NULL; 141 | while (h = read_header_field(in)) { 142 | h->next = req->header; 143 | req->header = h; 144 | } 145 | req->length = content_length(req); 146 | if (req->length != 0) { 147 | if (req->length > MAX_REQUEST_BODY_LENGTH) 148 | log_exit("request body too long"); 149 | req->body = xmalloc(req->length); 150 | if (fread(req->body, req->length, 1, in) < 1) 151 | log_exit("failed to read request body"); 152 | } else { 153 | req->body = NULL; 154 | } 155 | return req; 156 | } 157 | 158 | static void 159 | read_request_line(struct HTTPRequest *req, FILE *in) 160 | { 161 | char buf[LINE_BUF_SIZE]; 162 | char *path, *p; 163 | 164 | if (!fgets(buf, LINE_BUF_SIZE, in)) 165 | log_exit("no request line"); 166 | p = strchr(buf, ' '); /* p (1) */ 167 | if (!p) log_exit("parse error on request line (1): %s", buf); 168 | *p++ = '\0'; 169 | req->method = xmalloc(p - buf); 170 | strcpy(req->method, buf); 171 | upcase(req->method); 172 | 173 | path = p; 174 | p = strchr(path, ' '); /* p (2) */ 175 | if (!p) log_exit("parse error on request line (2): %s", buf); 176 | *p++ = '\0'; 177 | req->path = xmalloc(p - path); 178 | strcpy(req->path, path); 179 | 180 | if (strncasecmp(p, "HTTP/1.", strlen("HTTP/1.")) != 0) 181 | log_exit("parse error on request line (3): %s", buf); 182 | p += strlen("HTTP/1."); /* p (3) */ 183 | req->protocol_minor_version = atoi(p); 184 | } 185 | 186 | static struct HTTPHeaderField* 187 | read_header_field(FILE *in) 188 | { 189 | struct HTTPHeaderField *h; 190 | char buf[LINE_BUF_SIZE]; 191 | char *p; 192 | 193 | if (!fgets(buf, LINE_BUF_SIZE, in)) 194 | log_exit("failed to read request header field: %s", strerror(errno)); 195 | if ((buf[0] == '\n') || (strcmp(buf, "\r\n") == 0)) 196 | return NULL; 197 | 198 | p = strchr(buf, ':'); 199 | if (!p) log_exit("parse error on request header field: %s", buf); 200 | *p++ = '\0'; 201 | h = xmalloc(sizeof(struct HTTPHeaderField)); 202 | h->name = xmalloc(p - buf); 203 | strcpy(h->name, buf); 204 | 205 | p += strspn(p, " \t"); 206 | h->value = xmalloc(strlen(p) + 1); 207 | strcpy(h->value, p); 208 | 209 | return h; 210 | } 211 | 212 | static void 213 | upcase(char *str) 214 | { 215 | char *p; 216 | 217 | for (p = str; *p; p++) { 218 | *p = (char)toupper((int)*p); 219 | } 220 | } 221 | 222 | static void 223 | free_request(struct HTTPRequest *req) 224 | { 225 | struct HTTPHeaderField *h, *head; 226 | 227 | head = req->header; 228 | while (head) { 229 | h = head; 230 | head = head->next; 231 | free(h->name); 232 | free(h->value); 233 | free(h); 234 | } 235 | free(req->method); 236 | free(req->path); 237 | free(req->body); 238 | free(req); 239 | } 240 | 241 | static long 242 | content_length(struct HTTPRequest *req) 243 | { 244 | char *val; 245 | long len; 246 | 247 | val = lookup_header_field_value(req, "Content-Length"); 248 | if (!val) return 0; 249 | len = atol(val); 250 | if (len < 0) log_exit("negative Content-Length value"); 251 | return len; 252 | } 253 | 254 | static char* 255 | lookup_header_field_value(struct HTTPRequest *req, char *name) 256 | { 257 | struct HTTPHeaderField *h; 258 | 259 | for (h = req->header; h; h = h->next) { 260 | if (strcasecmp(h->name, name) == 0) 261 | return h->value; 262 | } 263 | return NULL; 264 | } 265 | 266 | static void 267 | respond_to(struct HTTPRequest *req, FILE *out, char *docroot) 268 | { 269 | if (strcmp(req->method, "GET") == 0) 270 | do_file_response(req, out, docroot); 271 | else if (strcmp(req->method, "HEAD") == 0) 272 | do_file_response(req, out, docroot); 273 | else if (strcmp(req->method, "POST") == 0) 274 | method_not_allowed(req, out); 275 | else 276 | not_implemented(req, out); 277 | } 278 | 279 | static void 280 | do_file_response(struct HTTPRequest *req, FILE *out, char *docroot) 281 | { 282 | struct FileInfo *info; 283 | 284 | info = get_fileinfo(docroot, req->path); 285 | if (!info->ok) { 286 | free_fileinfo(info); 287 | not_found(req, out); 288 | return; 289 | } 290 | output_common_header_fields(req, out, "200 OK"); 291 | fprintf(out, "Content-Length: %ld\r\n", info->size); 292 | fprintf(out, "Content-Type: %s\r\n", guess_content_type(info)); 293 | fprintf(out, "\r\n"); 294 | if (strcmp(req->method, "HEAD") != 0) { 295 | int fd; 296 | char buf[BLOCK_BUF_SIZE]; 297 | ssize_t n; 298 | 299 | fd = open(info->path, O_RDONLY); 300 | if (fd < 0) 301 | log_exit("failed to open %s: %s", info->path, strerror(errno)); 302 | for (;;) { 303 | n = read(fd, buf, BLOCK_BUF_SIZE); 304 | if (n < 0) 305 | log_exit("failed to read %s: %s", info->path, strerror(errno)); 306 | if (n == 0) 307 | break; 308 | if (fwrite(buf, 1, n, out) < n) 309 | log_exit("failed to write to socket"); 310 | } 311 | close(fd); 312 | } 313 | fflush(out); 314 | free_fileinfo(info); 315 | } 316 | 317 | static void 318 | method_not_allowed(struct HTTPRequest *req, FILE *out) 319 | { 320 | output_common_header_fields(req, out, "405 Method Not Allowed"); 321 | fprintf(out, "Content-Type: text/html\r\n"); 322 | fprintf(out, "\r\n"); 323 | fprintf(out, "\r\n"); 324 | fprintf(out, "
\r\n"); 325 | fprintf(out, "405 Method Not Allowed\r\n"); 326 | fprintf(out, "
\r\n"); 327 | fprintf(out, "\r\n"); 328 | fprintf(out, "

The request method %s is not allowed

\r\n", req->method); 329 | fprintf(out, "\r\n"); 330 | fprintf(out, "\r\n"); 331 | fflush(out); 332 | } 333 | 334 | static void 335 | not_implemented(struct HTTPRequest *req, FILE *out) 336 | { 337 | output_common_header_fields(req, out, "501 Not Implemented"); 338 | fprintf(out, "Content-Type: text/html\r\n"); 339 | fprintf(out, "\r\n"); 340 | fprintf(out, "\r\n"); 341 | fprintf(out, "
\r\n"); 342 | fprintf(out, "501 Not Implemented\r\n"); 343 | fprintf(out, "
\r\n"); 344 | fprintf(out, "\r\n"); 345 | fprintf(out, "

The request method %s is not implemented

\r\n", req->method); 346 | fprintf(out, "\r\n"); 347 | fprintf(out, "\r\n"); 348 | fflush(out); 349 | } 350 | 351 | static void 352 | not_found(struct HTTPRequest *req, FILE *out) 353 | { 354 | output_common_header_fields(req, out, "404 Not Found"); 355 | fprintf(out, "Content-Type: text/html\r\n"); 356 | fprintf(out, "\r\n"); 357 | if (strcmp(req->method, "HEAD") != 0) { 358 | fprintf(out, "\r\n"); 359 | fprintf(out, "
Not Found
\r\n"); 360 | fprintf(out, "

File not found

\r\n"); 361 | fprintf(out, "\r\n"); 362 | } 363 | fflush(out); 364 | } 365 | 366 | #define TIME_BUF_SIZE 64 367 | 368 | static void 369 | output_common_header_fields(struct HTTPRequest *req, FILE *out, char *status) 370 | { 371 | time_t t; 372 | struct tm *tm; 373 | char buf[TIME_BUF_SIZE]; 374 | 375 | t = time(NULL); 376 | tm = gmtime(&t); 377 | if (!tm) log_exit("gmtime() failed: %s", strerror(errno)); 378 | strftime(buf, TIME_BUF_SIZE, "%a, %d %b %Y %H:%M:%S GMT", tm); 379 | fprintf(out, "HTTP/1.%d %s\r\n", HTTP_MINOR_VERSION, status); 380 | fprintf(out, "Date: %s\r\n", buf); 381 | fprintf(out, "Server: %s/%s\r\n", SERVER_NAME, SERVER_VERSION); 382 | fprintf(out, "Connection: close\r\n"); 383 | } 384 | 385 | static struct FileInfo* 386 | get_fileinfo(char *docroot, char *urlpath) 387 | { 388 | struct FileInfo *info; 389 | struct stat st; 390 | 391 | info = xmalloc(sizeof(struct FileInfo)); 392 | info->path = build_fspath(docroot, urlpath); 393 | info->ok = 0; 394 | if (lstat(info->path, &st) < 0) return info; 395 | if (!S_ISREG(st.st_mode)) return info; 396 | info->ok = 1; 397 | info->size = st.st_size; 398 | return info; 399 | } 400 | 401 | static char * 402 | build_fspath(char *docroot, char *urlpath) 403 | { 404 | char *path; 405 | 406 | path = xmalloc(strlen(docroot) + 1 + strlen(urlpath) + 1); 407 | sprintf(path, "%s/%s", docroot, urlpath); 408 | return path; 409 | } 410 | 411 | static void 412 | free_fileinfo(struct FileInfo *info) 413 | { 414 | free(info->path); 415 | free(info); 416 | } 417 | 418 | static char* 419 | guess_content_type(struct FileInfo *info) 420 | { 421 | return "text/plain"; /* FIXME */ 422 | } 423 | 424 | static void* 425 | xmalloc(size_t sz) 426 | { 427 | void *p; 428 | 429 | p = malloc(sz); 430 | if (!p) log_exit("failed to allocate memory"); 431 | return p; 432 | } 433 | 434 | static void 435 | log_exit(char *fmt, ...) 436 | { 437 | va_list ap; 438 | 439 | va_start(ap, fmt); 440 | vfprintf(stderr, fmt, ap); 441 | fputc('\n', stderr); 442 | va_end(ap); 443 | exit(1); 444 | } 445 | -------------------------------------------------------------------------------- /httpd2.c: -------------------------------------------------------------------------------- 1 | /* 2 | httpd2.c -- standalone HTTP 1.0 server 3 | 4 | Copyright (c) 2004,2005,2017 Minero Aoki 5 | 6 | This program is free software. 7 | Redistribution and use in source and binary forms, 8 | with or without modification, are permitted. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define _GNU_SOURCE 31 | #include 32 | 33 | /****** Constants ********************************************************/ 34 | 35 | #define SERVER_NAME "LittleHTTP" 36 | #define SERVER_VERSION "1.0" 37 | #define HTTP_MINOR_VERSION 0 38 | #define BLOCK_BUF_SIZE 1024 39 | #define LINE_BUF_SIZE 4096 40 | #define MAX_REQUEST_BODY_LENGTH (1024 * 1024) 41 | #define MAX_BACKLOG 5 42 | #define DEFAULT_PORT "80" 43 | 44 | /****** Data Type Definitions ********************************************/ 45 | 46 | struct HTTPHeaderField { 47 | char *name; 48 | char *value; 49 | struct HTTPHeaderField *next; 50 | }; 51 | 52 | struct HTTPRequest { 53 | int protocol_minor_version; 54 | char *method; 55 | char *path; 56 | struct HTTPHeaderField *header; 57 | char *body; 58 | long length; 59 | }; 60 | 61 | struct FileInfo { 62 | char *path; 63 | long size; 64 | int ok; 65 | }; 66 | 67 | /****** Function Prototypes **********************************************/ 68 | 69 | static void setup_environment(char *root, char *user, char *group); 70 | typedef void (*sighandler_t)(int); 71 | static void install_signal_handlers(void); 72 | static void trap_signal(int sig, sighandler_t handler); 73 | static void detach_children(void); 74 | static void signal_exit(int sig); 75 | static void noop_handler(int sig); 76 | static void become_daemon(void); 77 | static int listen_socket(char *port); 78 | static void server_main(int server, char *docroot); 79 | static void service(FILE *in, FILE *out, char *docroot); 80 | static struct HTTPRequest* read_request(FILE *in); 81 | static void read_request_line(struct HTTPRequest *req, FILE *in); 82 | static struct HTTPHeaderField* read_header_field(FILE *in); 83 | static void upcase(char *str); 84 | static void free_request(struct HTTPRequest *req); 85 | static long content_length(struct HTTPRequest *req); 86 | static char* lookup_header_field_value(struct HTTPRequest *req, char *name); 87 | static void respond_to(struct HTTPRequest *req, FILE *out, char *docroot); 88 | static void do_file_response(struct HTTPRequest *req, FILE *out, char *docroot); 89 | static void method_not_allowed(struct HTTPRequest *req, FILE *out); 90 | static void not_implemented(struct HTTPRequest *req, FILE *out); 91 | static void not_found(struct HTTPRequest *req, FILE *out); 92 | static void output_common_header_fields(struct HTTPRequest *req, FILE *out, char *status); 93 | static struct FileInfo* get_fileinfo(char *docroot, char *path); 94 | static char* build_fspath(char *docroot, char *path); 95 | static void free_fileinfo(struct FileInfo *info); 96 | static char* guess_content_type(struct FileInfo *info); 97 | static void* xmalloc(size_t sz); 98 | static void log_exit(const char *fmt, ...); 99 | 100 | /****** Functions ********************************************************/ 101 | 102 | #define USAGE "Usage: %s [--port=n] [--chroot --user=u --group=g] [--debug] \n" 103 | 104 | static int debug_mode = 0; 105 | 106 | static struct option longopts[] = { 107 | {"debug", no_argument, &debug_mode, 1}, 108 | {"chroot", no_argument, NULL, 'c'}, 109 | {"user", required_argument, NULL, 'u'}, 110 | {"group", required_argument, NULL, 'g'}, 111 | {"port", required_argument, NULL, 'p'}, 112 | {"help", no_argument, NULL, 'h'}, 113 | {0, 0, 0, 0} 114 | }; 115 | 116 | int 117 | main(int argc, char *argv[]) 118 | { 119 | int server_fd; 120 | char *port = NULL; 121 | char *docroot; 122 | int do_chroot = 0; 123 | char *user = NULL; 124 | char *group = NULL; 125 | int opt; 126 | 127 | while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { 128 | switch (opt) { 129 | case 0: 130 | break; 131 | case 'c': 132 | do_chroot = 1; 133 | break; 134 | case 'u': 135 | user = optarg; 136 | break; 137 | case 'g': 138 | group = optarg; 139 | break; 140 | case 'p': 141 | port = optarg; 142 | break; 143 | case 'h': 144 | fprintf(stdout, USAGE, argv[0]); 145 | exit(0); 146 | case '?': 147 | fprintf(stderr, USAGE, argv[0]); 148 | exit(1); 149 | } 150 | } 151 | if (optind != argc - 1) { 152 | fprintf(stderr, USAGE, argv[0]); 153 | exit(1); 154 | } 155 | docroot = argv[optind]; 156 | 157 | if (do_chroot) { 158 | setup_environment(docroot, user, group); 159 | docroot = ""; 160 | } 161 | install_signal_handlers(); 162 | server_fd = listen_socket(port); 163 | if (!debug_mode) { 164 | openlog(SERVER_NAME, LOG_PID|LOG_NDELAY, LOG_DAEMON); 165 | become_daemon(); 166 | } 167 | server_main(server_fd, docroot); 168 | exit(0); 169 | } 170 | 171 | static void 172 | setup_environment(char *root, char *user, char *group) 173 | { 174 | struct passwd *pw; 175 | struct group *gr; 176 | 177 | if (!user || !group) { 178 | fprintf(stderr, "use both of --user and --group\n"); 179 | exit(1); 180 | } 181 | gr = getgrnam(group); 182 | if (!gr) { 183 | fprintf(stderr, "no such group: %s\n", group); 184 | exit(1); 185 | } 186 | if (setgid(gr->gr_gid) < 0) { 187 | perror("setgid(2)"); 188 | exit(1); 189 | } 190 | if (initgroups(user, gr->gr_gid) < 0) { 191 | perror("initgroups(2)"); 192 | exit(1); 193 | } 194 | pw = getpwnam(user); 195 | if (!pw) { 196 | fprintf(stderr, "no such user: %s\n", user); 197 | exit(1); 198 | } 199 | chroot(root); 200 | if (setuid(pw->pw_uid) < 0) { 201 | perror("setuid(2)"); 202 | exit(1); 203 | } 204 | } 205 | 206 | static void 207 | become_daemon(void) 208 | { 209 | int n; 210 | 211 | if (chdir("/") < 0) 212 | log_exit("chdir(2) failed: %s", strerror(errno)); 213 | freopen("/dev/null", "r", stdin); 214 | freopen("/dev/null", "w", stdout); 215 | freopen("/dev/null", "w", stderr); 216 | n = fork(); 217 | if (n < 0) log_exit("fork(2) failed: %s", strerror(errno)); 218 | if (n != 0) _exit(0); 219 | if (setsid() < 0) log_exit("setsid(2) failed: %s", strerror(errno)); 220 | } 221 | 222 | static void 223 | install_signal_handlers(void) 224 | { 225 | trap_signal(SIGTERM, signal_exit); 226 | detach_children(); 227 | } 228 | 229 | static void 230 | trap_signal(int sig, sighandler_t handler) 231 | { 232 | struct sigaction act; 233 | 234 | act.sa_handler = handler; 235 | sigemptyset(&act.sa_mask); 236 | act.sa_flags = SA_RESTART; 237 | if (sigaction(sig, &act, NULL) < 0) { 238 | log_exit("sigaction() failed: %s", strerror(errno)); 239 | } 240 | } 241 | 242 | static void 243 | detach_children(void) 244 | { 245 | struct sigaction act; 246 | 247 | act.sa_handler = noop_handler; 248 | sigemptyset(&act.sa_mask); 249 | act.sa_flags = SA_RESTART | SA_NOCLDWAIT; 250 | if (sigaction(SIGCHLD, &act, NULL) < 0) { 251 | log_exit("sigaction() failed: %s", strerror(errno)); 252 | } 253 | } 254 | 255 | static void 256 | signal_exit(int sig) 257 | { 258 | log_exit("exit by signal %d", sig); 259 | } 260 | 261 | static void 262 | noop_handler(int sig) 263 | { 264 | ; 265 | } 266 | 267 | static int 268 | listen_socket(char *port) 269 | { 270 | struct addrinfo hints, *res, *ai; 271 | int err; 272 | 273 | memset(&hints, 0, sizeof(struct addrinfo)); 274 | hints.ai_family = AF_INET; 275 | hints.ai_socktype = SOCK_STREAM; 276 | hints.ai_flags = AI_PASSIVE; 277 | if ((err = getaddrinfo(NULL, port, &hints, &res)) != 0) 278 | log_exit(gai_strerror(err)); 279 | for (ai = res; ai; ai = ai->ai_next) { 280 | int sock; 281 | 282 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 283 | if (sock < 0) continue; 284 | if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { 285 | close(sock); 286 | continue; 287 | } 288 | if (listen(sock, MAX_BACKLOG) < 0) { 289 | close(sock); 290 | continue; 291 | } 292 | freeaddrinfo(res); 293 | return sock; 294 | } 295 | log_exit("failed to listen socket"); 296 | return -1; /* NOT REACH */ 297 | } 298 | 299 | static void 300 | server_main(int server_fd, char *docroot) 301 | { 302 | for (;;) { 303 | struct sockaddr_storage addr; 304 | socklen_t addrlen = sizeof addr; 305 | int sock; 306 | int pid; 307 | 308 | sock = accept(server_fd, (struct sockaddr*)&addr, &addrlen); 309 | if (sock < 0) log_exit("accept(2) failed: %s", strerror(errno)); 310 | pid = fork(); 311 | if (pid < 0) exit(3); 312 | if (pid == 0) { /* child */ 313 | FILE *inf = fdopen(sock, "r"); 314 | FILE *outf = fdopen(sock, "w"); 315 | 316 | service(inf, outf, docroot); 317 | exit(0); 318 | } 319 | close(sock); 320 | } 321 | } 322 | 323 | static void 324 | service(FILE *in, FILE *out, char *docroot) 325 | { 326 | struct HTTPRequest *req; 327 | 328 | req = read_request(in); 329 | respond_to(req, out, docroot); 330 | free_request(req); 331 | } 332 | 333 | static struct HTTPRequest* 334 | read_request(FILE *in) 335 | { 336 | struct HTTPRequest *req; 337 | struct HTTPHeaderField *h; 338 | 339 | req = xmalloc(sizeof(struct HTTPRequest)); 340 | read_request_line(req, in); 341 | req->header = NULL; 342 | while (h = read_header_field(in)) { 343 | h->next = req->header; 344 | req->header = h; 345 | } 346 | req->length = content_length(req); 347 | if (req->length != 0) { 348 | if (req->length > MAX_REQUEST_BODY_LENGTH) 349 | log_exit("request body too long"); 350 | req->body = xmalloc(req->length); 351 | if (fread(req->body, req->length, 1, in) < 1) 352 | log_exit("failed to read request body"); 353 | } else { 354 | req->body = NULL; 355 | } 356 | return req; 357 | } 358 | 359 | static void 360 | read_request_line(struct HTTPRequest *req, FILE *in) 361 | { 362 | char buf[LINE_BUF_SIZE]; 363 | char *path, *p; 364 | 365 | if (!fgets(buf, LINE_BUF_SIZE, in)) 366 | log_exit("no request line"); 367 | p = strchr(buf, ' '); /* p (1) */ 368 | if (!p) log_exit("parse error on request line (1): %s", buf); 369 | *p++ = '\0'; 370 | req->method = xmalloc(p - buf); 371 | strcpy(req->method, buf); 372 | upcase(req->method); 373 | 374 | path = p; 375 | p = strchr(path, ' '); /* p (2) */ 376 | if (!p) log_exit("parse error on request line (2): %s", buf); 377 | *p++ = '\0'; 378 | req->path = xmalloc(p - path); 379 | strcpy(req->path, path); 380 | 381 | if (strncasecmp(p, "HTTP/1.", strlen("HTTP/1.")) != 0) 382 | log_exit("parse error on request line (3): %s", buf); 383 | p += strlen("HTTP/1."); /* p (3) */ 384 | req->protocol_minor_version = atoi(p); 385 | } 386 | 387 | static struct HTTPHeaderField* 388 | read_header_field(FILE *in) 389 | { 390 | struct HTTPHeaderField *h; 391 | char buf[LINE_BUF_SIZE]; 392 | char *p; 393 | 394 | if (!fgets(buf, LINE_BUF_SIZE, in)) 395 | log_exit("failed to read request header field: %s", strerror(errno)); 396 | if ((buf[0] == '\n') || (strcmp(buf, "\r\n") == 0)) 397 | return NULL; 398 | 399 | p = strchr(buf, ':'); 400 | if (!p) log_exit("parse error on request header field: %s", buf); 401 | *p++ = '\0'; 402 | h = xmalloc(sizeof(struct HTTPHeaderField)); 403 | h->name = xmalloc(p - buf); 404 | strcpy(h->name, buf); 405 | 406 | p += strspn(p, " \t"); 407 | h->value = xmalloc(strlen(p) + 1); 408 | strcpy(h->value, p); 409 | 410 | return h; 411 | } 412 | 413 | static void 414 | upcase(char *str) 415 | { 416 | char *p; 417 | 418 | for (p = str; *p; p++) { 419 | *p = (char)toupper((int)*p); 420 | } 421 | } 422 | 423 | static void 424 | free_request(struct HTTPRequest *req) 425 | { 426 | struct HTTPHeaderField *h, *head; 427 | 428 | head = req->header; 429 | while (head) { 430 | h = head; 431 | head = head->next; 432 | free(h->name); 433 | free(h->value); 434 | free(h); 435 | } 436 | free(req->method); 437 | free(req->path); 438 | free(req->body); 439 | free(req); 440 | } 441 | 442 | static long 443 | content_length(struct HTTPRequest *req) 444 | { 445 | char *val; 446 | long len; 447 | 448 | val = lookup_header_field_value(req, "Content-Length"); 449 | if (!val) return 0; 450 | len = atol(val); 451 | if (len < 0) log_exit("negative Content-Length value"); 452 | return len; 453 | } 454 | 455 | static char* 456 | lookup_header_field_value(struct HTTPRequest *req, char *name) 457 | { 458 | struct HTTPHeaderField *h; 459 | 460 | for (h = req->header; h; h = h->next) { 461 | if (strcasecmp(h->name, name) == 0) 462 | return h->value; 463 | } 464 | return NULL; 465 | } 466 | 467 | static void 468 | respond_to(struct HTTPRequest *req, FILE *out, char *docroot) 469 | { 470 | if (strcmp(req->method, "GET") == 0) 471 | do_file_response(req, out, docroot); 472 | else if (strcmp(req->method, "HEAD") == 0) 473 | do_file_response(req, out, docroot); 474 | else if (strcmp(req->method, "POST") == 0) 475 | method_not_allowed(req, out); 476 | else 477 | not_implemented(req, out); 478 | } 479 | 480 | static void 481 | do_file_response(struct HTTPRequest *req, FILE *out, char *docroot) 482 | { 483 | struct FileInfo *info; 484 | 485 | info = get_fileinfo(docroot, req->path); 486 | if (!info->ok) { 487 | free_fileinfo(info); 488 | not_found(req, out); 489 | return; 490 | } 491 | output_common_header_fields(req, out, "200 OK"); 492 | fprintf(out, "Content-Length: %ld\r\n", info->size); 493 | fprintf(out, "Content-Type: %s\r\n", guess_content_type(info)); 494 | fprintf(out, "\r\n"); 495 | if (strcmp(req->method, "HEAD") != 0) { 496 | int fd; 497 | char buf[BLOCK_BUF_SIZE]; 498 | ssize_t n; 499 | 500 | fd = open(info->path, O_RDONLY); 501 | if (fd < 0) 502 | log_exit("failed to open %s: %s", info->path, strerror(errno)); 503 | for (;;) { 504 | n = read(fd, buf, BLOCK_BUF_SIZE); 505 | if (n < 0) 506 | log_exit("failed to read %s: %s", info->path, strerror(errno)); 507 | if (n == 0) 508 | break; 509 | if (fwrite(buf, 1, n, out) < n) 510 | log_exit("failed to write to socket"); 511 | } 512 | close(fd); 513 | } 514 | fflush(out); 515 | free_fileinfo(info); 516 | } 517 | 518 | static void 519 | method_not_allowed(struct HTTPRequest *req, FILE *out) 520 | { 521 | output_common_header_fields(req, out, "405 Method Not Allowed"); 522 | fprintf(out, "Content-Type: text/html\r\n"); 523 | fprintf(out, "\r\n"); 524 | fprintf(out, "\r\n"); 525 | fprintf(out, "
\r\n"); 526 | fprintf(out, "405 Method Not Allowed\r\n"); 527 | fprintf(out, "
\r\n"); 528 | fprintf(out, "\r\n"); 529 | fprintf(out, "

The request method %s is not allowed

\r\n", req->method); 530 | fprintf(out, "\r\n"); 531 | fprintf(out, "\r\n"); 532 | fflush(out); 533 | } 534 | 535 | static void 536 | not_implemented(struct HTTPRequest *req, FILE *out) 537 | { 538 | output_common_header_fields(req, out, "501 Not Implemented"); 539 | fprintf(out, "Content-Type: text/html\r\n"); 540 | fprintf(out, "\r\n"); 541 | fprintf(out, "\r\n"); 542 | fprintf(out, "
\r\n"); 543 | fprintf(out, "501 Not Implemented\r\n"); 544 | fprintf(out, "
\r\n"); 545 | fprintf(out, "\r\n"); 546 | fprintf(out, "

The request method %s is not implemented

\r\n", req->method); 547 | fprintf(out, "\r\n"); 548 | fprintf(out, "\r\n"); 549 | fflush(out); 550 | } 551 | 552 | static void 553 | not_found(struct HTTPRequest *req, FILE *out) 554 | { 555 | output_common_header_fields(req, out, "404 Not Found"); 556 | fprintf(out, "Content-Type: text/html\r\n"); 557 | fprintf(out, "\r\n"); 558 | if (strcmp(req->method, "HEAD") != 0) { 559 | fprintf(out, "\r\n"); 560 | fprintf(out, "
Not Found
\r\n"); 561 | fprintf(out, "

File not found

\r\n"); 562 | fprintf(out, "\r\n"); 563 | } 564 | fflush(out); 565 | } 566 | 567 | #define TIME_BUF_SIZE 64 568 | 569 | static void 570 | output_common_header_fields(struct HTTPRequest *req, FILE *out, char *status) 571 | { 572 | time_t t; 573 | struct tm *tm; 574 | char buf[TIME_BUF_SIZE]; 575 | 576 | t = time(NULL); 577 | tm = gmtime(&t); 578 | if (!tm) log_exit("gmtime() failed: %s", strerror(errno)); 579 | strftime(buf, TIME_BUF_SIZE, "%a, %d %b %Y %H:%M:%S GMT", tm); 580 | fprintf(out, "HTTP/1.%d %s\r\n", HTTP_MINOR_VERSION, status); 581 | fprintf(out, "Date: %s\r\n", buf); 582 | fprintf(out, "Server: %s/%s\r\n", SERVER_NAME, SERVER_VERSION); 583 | fprintf(out, "Connection: close\r\n"); 584 | } 585 | 586 | static struct FileInfo* 587 | get_fileinfo(char *docroot, char *urlpath) 588 | { 589 | struct FileInfo *info; 590 | struct stat st; 591 | 592 | info = xmalloc(sizeof(struct FileInfo)); 593 | info->path = build_fspath(docroot, urlpath); 594 | info->ok = 0; 595 | if (lstat(info->path, &st) < 0) return info; 596 | if (!S_ISREG(st.st_mode)) return info; 597 | info->ok = 1; 598 | info->size = st.st_size; 599 | return info; 600 | } 601 | 602 | static char * 603 | build_fspath(char *docroot, char *urlpath) 604 | { 605 | char *path; 606 | 607 | path = xmalloc(strlen(docroot) + 1 + strlen(urlpath) + 1); 608 | sprintf(path, "%s/%s", docroot, urlpath); 609 | return path; 610 | } 611 | 612 | static void 613 | free_fileinfo(struct FileInfo *info) 614 | { 615 | free(info->path); 616 | free(info); 617 | } 618 | 619 | static char* 620 | guess_content_type(struct FileInfo *info) 621 | { 622 | return "text/plain"; /* FIXME */ 623 | } 624 | 625 | static void* 626 | xmalloc(size_t sz) 627 | { 628 | void *p; 629 | 630 | p = malloc(sz); 631 | if (!p) log_exit("failed to allocate memory"); 632 | return p; 633 | } 634 | 635 | static void 636 | log_exit(const char *fmt, ...) 637 | { 638 | va_list ap; 639 | 640 | va_start(ap, fmt); 641 | if (debug_mode) { 642 | vfprintf(stderr, fmt, ap); 643 | fputc('\n', stderr); 644 | } 645 | else { 646 | vsyslog(LOG_ERR, fmt, ap); 647 | } 648 | va_end(ap); 649 | exit(1); 650 | } 651 | --------------------------------------------------------------------------------