├── 00.book ├── chapter01_more ├── chapter02_who ├── chapter03_ls ├── chapter04_pwd ├── chapter05_stty ├── chapter06_signal ├── chapter07_vediogame ├── chapter08_sh ├── chapter09_shell ├── chapter10_io ├── chapter11_socket ├── chapter12_web ├── chapter13_datagram ├── chapter14_thread └── chapter15_IPC ├── 01.more ├── more01 ├── more01.c ├── more02 └── more02.c ├── 02.who ├── cp1 ├── cp1.c ├── logout_tty.c ├── utmplib.c ├── who1 ├── who1.c ├── who2 ├── who2.c ├── who3 └── who3.c ├── 03.ls ├── ls1 ├── ls1.c ├── ls2 ├── ls2.c ├── ls3.c ├── malloc_element ├── malloc_element.c ├── my_sort ├── my_sort.cpp ├── test ├── test.c ├── ttt └── ttt.c ├── 04.pwd ├── spwd └── spwd.c ├── 05.stty ├── echostate ├── echostate.c ├── setecho ├── setecho.c ├── showtty ├── showtty.c ├── write0 └── write0.c ├── 06.signal ├── play_again0 ├── play_again0.c ├── play_again1 ├── play_again1.c ├── play_again2 ├── play_again2.c ├── play_again3 ├── play_again3.c ├── rotate ├── rotate.c ├── sigdemo1 ├── sigdemo1.c ├── sigdemo2 └── sigdemo2.c ├── 07.videogame ├── bounce.h ├── bounce1d ├── bounce1d.c ├── bounce2d ├── bounce2d.c ├── bounce_aio ├── bounce_aio.c ├── bounce_async ├── bounce_async.c ├── hello1 ├── hello1.c ├── hello2 ├── hello2.c ├── hello3 ├── hello3.c ├── hello4 ├── hello4.c ├── hello5 ├── hello5.c ├── sigactdemo ├── sigactdemo.c ├── sigdemo3 ├── sigdemo3.c ├── sleep1 ├── sleep1.c ├── ticker_demo └── ticker_demo.c ├── 08.sh ├── psh1 ├── psh1.c ├── psh2 └── psh2.c ├── 09.shell ├── smsh1 │ ├── execute.c │ ├── smsh.h │ ├── smsh1 │ ├── smsh1.c │ └── splitline.c ├── smsh2 │ ├── controlflow.c │ ├── execute.c │ ├── process.c │ ├── smsh.h │ ├── smsh2 │ ├── smsh2.c │ └── splitline.c ├── smsh3 │ ├── builtin.c │ ├── controlflow.c │ ├── execute.c │ ├── process.c │ ├── smsh.h │ ├── smsh3 │ ├── smsh3.c │ ├── splitline.c │ ├── varlib.c │ └── varlib.h └── smsh4 │ ├── builtin.c │ ├── controlflow.c │ ├── execute.c │ ├── process.c │ ├── smsh.h │ ├── smsh4 │ ├── smsh4.c │ ├── splitline.c │ ├── varlib.c │ └── varlib.h ├── 10.io ├── data ├── pipe ├── pipe.c ├── pipedemo ├── pipedemo.c ├── pipedemo2 ├── pipedemo2.c ├── stdinredir1 ├── stdinredir1.c ├── stdinredir2 ├── stdinredir2.c ├── userlist ├── whotofile └── whotofile.c ├── 11.socket ├── popen.c ├── timeclnt ├── timeclnt.c ├── timeserv ├── timeserv.c ├── tinybc └── tinybc.c ├── 12.web ├── hello.cgi ├── socklib.c ├── webserv └── webserv.c ├── 13.udp ├── clnt ├── clnt.c ├── clnt_func.c ├── dgram.c ├── serv ├── serv.c └── serv_func.c ├── 14.thread ├── test_cond ├── test_cond.c ├── test_mutex ├── test_mutex.c ├── test_thread └── test_thread.c ├── 15.IPC ├── file.txt ├── file_tc ├── file_tc.c ├── file_ts ├── file_ts.c ├── ipc.c ├── shm_tc ├── shm_tc.c ├── shm_ts └── shm_ts.c └── README.md /00.book/chapter01_more: -------------------------------------------------------------------------------- 1 | 第一章 Unix系统编程概述 2 | 3 | 程序中所有对设备的操作都是通过内核进行的 4 | 5 | 在登陆过程中,当用户名和密码通过验证后,系统会启动一个叫做shell的进程,然后把 6 | 用户交给这个进程,由这个进程处理用户的请求,每个用户都有属于自己的shell进程 7 | 8 | ps命令可以列出系统中运行的所有进程 9 | 10 | 自己动手实践一个more,用来查看文件 11 | 12 | Unix编程不是很难,但也不是轻而易举的事情 13 | 14 | 计算机系统中包含了很多系统资源,如硬盘,内存,外围设备,网络连接等,程序利用 15 | 这些资源来对数据进行存储,转换和处理 16 | 17 | 多用户系统需要一个中央管理程序,Unix的内核就是这样的程序,它可以对程序和资源进行管理 18 | 19 | 用户程序访问设备必须通过内核 20 | 21 | 一些Unix的系统功能是由多个程序的协作而实现的 22 | 23 | 要编写系统程序,必须对系统调用和相关的数据结构有深入的理解 24 | -------------------------------------------------------------------------------- /00.book/chapter02_who: -------------------------------------------------------------------------------- 1 | 第2章 用户 文件操作与联机帮助:编写who命令 2 | 3 | who 命令通过读系统日志的内容显示当前已登陆的用户 4 | 5 | Unix 系统把数据存放在文件中,可以通过以下系统调用来操作文件: 6 | open(filename, how) 7 | creat(filename, mode) 8 | read(fd, buffer, amt) 9 | write(fd, buffer, amt) 10 | lseek(fd, distance, base) 11 | close(fd) 12 | 13 | 进程对文件的读写都要通过文件描述符,文件描述符表示文件与进程之间的连接 14 | 15 | 每次系统调用都会导致用户模式与内核模式的切换以及执行内核代码,所以减少 16 | 程序中的系统调用的次数可以提高程序的运行效率 17 | 18 | 程序可以通过缓冲技术来减少系统调用的次数,仅当写缓冲区满或者读缓冲区空时才调用 19 | 内核服务 20 | 21 | Unix 内核可以通过内核缓冲来减少访问磁盘IO 的次数 22 | 23 | Unix 中时间的处理方式是记录从某一个时间开始经过的秒数 24 | 25 | 当系统调用出错时会把全局变量errno 的值设为相应的错误代码,然后返回-1 程序可以 26 | 通过检查errno 来确定错误的类型,并采取相应的措施 27 | 28 | 这一章涉及的知识在系统中都可以找到,联机帮助中有命令的说明,有些还会涉及命令的 29 | 实现,头文件中有结构和系统常量的定义,还有函数原型的说明 30 | 31 | 在Unix 中增加命令很简单,只要把程序的可执行文件放在以下任意目录即可: 32 | /bin /usr/bin /usr/local/bin 或者通过alias 添加到~/.bashrc 33 | 34 | 使用系统调用open 来打开文件,如果文件被顺利打开,内核会返回一个正整数的值, 35 | 这个数值就叫做文件描述符 36 | 37 | Unix 中时间是以一个整数来表示,他的数值是从1970 1 1 0时开始所经过的秒数, 38 | 定义在time.h 中,typedef long int time_t; 39 | 40 | 系统调用是需要时间的,程序中频繁的系统调用会降低程序的运行效率 41 | 42 | 应用缓冲技术对提高系统的效率是明显的,他的主要思想是一次读入大量的数据放入缓冲区, 43 | 需要的时候从缓冲区取得数据 44 | 45 | 调用系统调用或者API 要有出错处理 46 | -------------------------------------------------------------------------------- /00.book/chapter03_ls: -------------------------------------------------------------------------------- 1 | 第三章 目录与文件属性:编写ls 2 | 3 | 磁盘上有文件和目录,文件和目录都有目录和属性.文件的内容可以是任意的数据, 4 | 目录的内容只能是文件名或者子目录名的属性 5 | 6 | 目录中的文件名和子目录名指向文件和其他的目录,内核提供了系统调用来读取目录的 7 | 内容,读取和修改文件的属性 8 | 9 | 文件类型,文件的访问权限和特殊属性被编码存储在一个16位整数中,可以通过 10 | 掩码技术来读取这些信息 11 | 12 | 文件所有者和组信息是以ID的形势保存的,它们与用户名和组名的联系保存在 13 | passwd和group数据库中 14 | 15 | 16 | 自己编写ls,需要掌握三点: 17 | 如何读取目录的内容 18 | 如何读取并显示文件的属性 19 | 给出一个名字,如何判断是目录还是文件 20 | 21 | 把多种信息编码到不同的字段是一种常用的技术,如电话号码,IP字段等 22 | 23 | 为了比较,把不需要的地方置为0,这种技术称为掩码 24 | 25 | 将二进制数的每三位分为一组来操作,这就是八进制 26 | 27 | 结构stat 中的st_mode 成员包含16位,其中四位用作文件类型,九位用作许可权限, 28 | 剩下的三位用作文件特殊属性 29 | set-user-ID s 使用它来给某些程序提供额外的权限,比如系统中的打印队列 30 | set-group-ID s 31 | sticky 它告诉内核,即使没有人使用程序,也要把它放在交换空间中,因为加载速度 32 | 比从硬盘空间快 33 | 在许可权限部分,用户的x被替换成s,代表set-user-ID 被设置 34 | 组用户的x被替换成s,代表set-group-ID被设置 35 | 其他用户的x被替换成t,代表sticky被设置 36 | -------------------------------------------------------------------------------- /00.book/chapter04_pwd: -------------------------------------------------------------------------------- 1 | 第四章 文件系统:编写pwd 2 | 3 | Unix将存储在磁盘中的数据组织成文件系统.文件系统是文件和 4 | 目录的组合,目录是名字和指针的列表.目录中的每一个入口指向 5 | 一个文件或目录.目录包含指向父目录和子目录的入口 6 | 7 | Unix文件系统包含三个主要部份:超级块,i-节点和数据区域. 8 | 文件内容存储在数据块.文件属性存储在i-节点.表中i-节点 9 | 的位置称为文件的i-节点号,i-节点号是文件的唯一标识 10 | 11 | 相同的i-节点号可能以不同的名字出现在若干个目录中.每个入口 12 | 被称为指向文件的硬链接.符号链接是通过文件名引用文件,而不是 13 | i-节点号 14 | 15 | 若干个文件系统的目录树可被整合成一棵树.内核将一个文件系统的 16 | 目录链接到另一个文件系统的根的操作称为装载 17 | 18 | Unix包含若干种系统调用,允许程序员进行创建和删除目录,复制指针 19 | 删除指针,改变链接和分离其他文件系统等的操作 20 | 21 | 目录与文件操作相关的系统调用: 22 | 创建目录  mkdir 23 | 删除目录 rmdir 24 | 删除文件  unlink 25 | 创建文件链接  link 26 | 改变文件或目录的名字和位置  rename 27 | 改变进程的当前目录  chdir 28 | 29 | -------------------------------------------------------------------------------- /00.book/chapter05_stty: -------------------------------------------------------------------------------- 1 | 第5章 连接控制:学习stty 2 | 3 | 内核在进程与外部世界之间交换数据.外部世界包括磁盘文件,终端与外部 4 | 设备,磁盘文件与终端的链接有相似之处也有差异 5 | 6 | 磁盘文件与设备文件都有名字,属性,和权限位.标准文件系统调用open,read 7 | write,close,lseek可被用于人物文件与设备.文件权限位以同样的方式应用于 8 | 控制设备文件和磁盘文件的关闭 9 | 10 | 到磁盘文件的连接在处理和传输数据方面不同于到设备文件的连接.内核中 11 | 管理与设备链接的代码被称为设备驱动程序.通过使用fcntl ioctl,进程 12 | 可以读取和改变设备驱动程序的设置 13 | 14 | 到终端的链接是如此的重要,以致函数tcgetattr tcsetattr 专门用来提供 15 | 对终端驱动器的控制 16 | 17 | Unix命令stty使得用户能够访问tcgetattr tcsetattr函数 18 | 19 | 测试位 if (flagset & MASK)... 20 | 置位flagset |= MASK 21 | 清除位flagset &= ~MASK 22 | -------------------------------------------------------------------------------- /00.book/chapter06_signal: -------------------------------------------------------------------------------- 1 | 第6章 为用户编程:终端控制和信号 2 | 3 | 有些程序处理从特定设备来的数据.这些与特定设备相关的程序 4 | 必须控制与设备的链接.Unix系统中最常见的设备是终端 5 | 6 | 终端驱动程序有很多设置.各个设置的特定值决定了终端驱动程序的模式. 7 | 为用户编写的程序通常需要设置终端驱动程序为特定的模式 8 | 9 | 键盘输入分为三类,终端驱动程序对这些输入做不同的处理.大多数键 10 | 代表常规数据,他们从驱动程序传输到程序,有些键调用驱动程序中的编辑 11 | 函数.如果按下删除键,驱动程序将前一个字符从他的行缓冲中删除,并将 12 | 命令发送到终端屏幕,使之从显示器中删除字符.最后,有些键调用处理 13 | 控制函数.Ctrl-C键告诉驱动程序调用内核中某个函数,这个函数给进程 14 | 发送一个信号.终端驱动程序支持若干种处理控制函数,他们都通过发送信号到 15 | 进程来实现控制 16 | 17 | 信号是从内核发送给进程的一种简短消息.信号可能来自用户,其他进程,或 18 | 内核本身.进程可以告诉内核,在他收到信号时需要做出怎样的响应 19 | 20 | 终端模式: 21 | 1 规范模式 22 | 常见模式,驱动程序输入的字符保存在缓冲,接收到回车才发送到程序 23 | 24 | 2 非规范模式 25 | 缓冲和编辑功能被关闭.stty -icanon 26 | 27 | 3 raw模式 28 | 每个处理步骤都被一个独立的位控制 29 | 30 | 由进程的某个操作产生的信号被称为同步信号 synchronous signals 31 | 由像用户击键这样的进程外的事件引起的信号被称为异步信号 asynchronous signals 32 | 33 | 进程如何处理信号: 34 | 1 接受默认处理 35 | 2 忽略信号 36 | 3 调用一个函数 37 | 38 | 大多数signal都可以被捕获或者忽略,但有两个无法被忽略,是SIGKILL SIGSTOP 39 | -------------------------------------------------------------------------------- /00.book/chapter07_vediogame: -------------------------------------------------------------------------------- 1 | 第7章 事件驱动编程:编写一个视频游戏 2 | 3 | 有些程序的控制流很简单.而另外一些则要响应外部的事件.一个 4 | 视频游戏要响应时钟和用户输入,操作系统也要响应时钟和外设 5 | 6 | curses库有一些可以管理屏幕显示字符的函数 7 | 8 | 一个进程通过设置计时器来安排事件.每个进程有三个独立的计时器. 9 | 计时器通过发送信号来通知进程.每个计时器都可以被设置为只发送 10 | 一次信号,或者按固定的间隙发送信号 11 | 12 | 处理一个信号很简单.同时处理多个信号就复杂了.进程能决定是忽略 13 | 信号还是阻塞信号.进程能告知内核哪些信号在什么时候阻塞或忽略 14 | 15 | 有些函数执行一些复杂的任务是不能被打断的.程序可以通过小心地 16 | 使用信号掩码来保护这些临界区代码 17 | 18 | curses库基本函数: 19 | initscr() 初始化curses库和tty 20 | endwin() 关闭curses并重置tty 21 | refresh() 使屏幕按照你的意图显示 22 | move(r, c) 移动光标到屏幕的r c位置 23 | addstr(s) 在当前位置画字符串s 24 | mvaddch(r,c,'s') 25 | clear() 清屏 26 | standout() 启动standout模式(一般使屏幕反色) 27 | standend() 关闭standout模式 28 | 29 | 30 | 调用pause 可以挂起进程直到有一个信号被处理 31 | 32 | Unix很早就有sleep alarm,但他们的精度是秒,后来有了一个新的 33 | 系统,叫间隔计时器interval timer,有更高的精度 usleep(n)n为微秒 34 | 三个计时器分别是: 35 | 真实 ITIMER_REAL 执行用户代码与内核代码所用时间 36 | 进程 ITIMER_VIRTUAL 用户态运行时间 37 | 实用 ITIMER_PROF 38 | 39 | 虽然每个进程有三个独立的计时器,但其实每个系统只需要一个时钟来 40 | 设置节拍.每当内核收到系统时钟脉冲,他遍历所有的间隔计时器, 41 | 使每个计数器减去一个时钟单位,当某进程计数器为0,则内核发送SIGALRM 42 | 给此进程. 43 | 44 | 一段修改一个数据结构的代码如果在运行时被打断将导致数据得不完整或损毁, 45 | 则称这段代码为临界区,临界区需要保护,最简单办法就是阻塞或者忽略那些 46 | 处理函数将要使用或修改特定数据的信号. 47 | 48 | kill向一个进程发送一个信号,两个进程用户ID必须一样,或者发送者是 49 | 超级用户 50 | -------------------------------------------------------------------------------- /00.book/chapter08_sh: -------------------------------------------------------------------------------- 1 | 第8章 进程和程序:编写命令解释器sh 2 | 3 | Unix通过将可执行代码装入进程并执行它来运行一个程序.进程是 4 | 一个程序所需的内存空间和其他资源的集合 5 | 6 | 每个运行中的程序在自己的进程中运行.每个进程都有一个唯一的 7 | 进程ID,所有者,大小及其他属性 8 | 9 | 系统调用fork通过复制进程来建立一个几乎和原来进程完全相同的 10 | 副本进程.这个新建的进程被称为子进程 11 | 12 | 一个程序通过调用exec函数族在当前进程中执行一个新的程序 13 | 14 | 一个程序能通过调用wait来等待子进程的结束 15 | 16 | 调用程序能将一个字符串列表传给新程序的main函数.新的程序能通过 17 | 调用exit来回传一个8位长的值 18 | 19 | Unix shell通过调用fork, exec, wait来运行程序 20 | 21 | 一个程序是存储在文件中的机器指令集合.一般它是由编译器将源代码 22 | 编译成二进制格式的代码.运行一个程序意味着将这个机器指令序列载入 23 | 内存然后让处理器逐条执行这些指令 24 | 25 | 可以通过命令ps(process status)来学习进程 26 | 27 | shell 是如何运行程序的: 28 | 1 用户键入a.out 29 | 2 shell建立一个新的进程来运行程序 30 | 3 shell将程序从磁盘载入 31 | 4 程序在它的进程中运行直到结束 32 | 33 | 要学会写shell,需要学会: 34 | 1 运行一个程序 35 | 2 建立一个进程 36 | 3 等待exit() 37 | 38 | 一个程序运行另一个程序: 39 | 调用execvp 40 | 内核将新程序载入到当前进程,替代当前进程的代码和数据,因此最好 41 | 还是fork一个子进程,然后子进程来调用execvp 42 | 43 | 如何建立新进程: 44 | fork 45 | 系统调用fork正是解决shell只能运行一条命令这个问题所需要的 46 | 47 | 父进程等待子进程结束: 48 | 进程调用wait等待子进程结束 pid = wait(&status); 49 | wait暂停调用它的进程直到子进程结束 最终子进程会结束任务并调用 50 | exit(n) 51 | 52 | 键盘信号发给所有连接的进程 53 | 54 | execvp/exit <=> call/return 55 | 这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序 56 | 设计的基础 57 | 58 | 全局变量和fork/exec 59 | 全局变量会破坏封装原则,但有时候去掉会更糟糕.Unix提供方法来建立 60 | 全局变量,环境是一些传递给进程的字符串型变量集合 61 | 62 | execvp不是一个系统调用,而是一个库函数,这个函数通过execve来 63 | 调用内核服务 64 | -------------------------------------------------------------------------------- /00.book/chapter09_shell: -------------------------------------------------------------------------------- 1 | 第9章 可编程的shell,shell变量和环境:编写自己的shell 2 | 3 | Unix shell 运行一种成为脚本的程序.一个shell脚本可以运行程序,接受 4 | 用户输入,使用变量和使用复杂的控制逻辑 5 | 6 | if..then 语句依赖于下属惯例:Unix程序返回0以表示成功.shell使用 7 | wait来得到程序的退出状态 8 | 9 | shell编程语言包括变量.这些变量存储字符串,他们可以在任何命令中使用.shell 10 | 变量是脚本的局部变量 11 | 12 | 每个程序都从调用它的进程中继承一个字符串列表,这个列表被称为环境. 13 | 环境用来保存会话(session)的全局设置和某个程序的参数设置,shell允许 14 | 用户查看和修改环境 15 | 16 | shell是有个编程语言解释器,这个解释器解释从键盘输入的命令,也解释 17 | 存储在脚本中的命令序列 18 | 19 | shell包括两类变量:局部变量和环境变量 20 | 对变量的操作: 21 | 赋值 var=value 22 | 引用 $var 23 | 删除 unset var 24 | 输入 read var 25 | 列出变量 set 26 | 全局化 export var 27 | -------------------------------------------------------------------------------- /00.book/chapter10_io: -------------------------------------------------------------------------------- 1 | 第10章 I/O重定向和管道 2 | 3 | 输入/输出重定向允许完成特定功能的程序通过交换数据来进行相互协作 4 | 5 | Unix默认规定程序从文件描述符0读取数据,写数据到文件描述符1,将 6 | 错误信息输出到文件描述符2.这三个文件描述符称为标准输入,标准输出 7 | 和标准错误输出 8 | 9 | 当登陆到Unix系统中,登陆程序设置文件描述符0,1,2.所有的连接, 10 | 文件描述符都会从父进程传递到子进程.他们也会在调用exec时被传递 11 | 12 | 创建文件描述符的系统调用总是使用最低可用文件描述符号 13 | 14 | 重定向标准输入,输出以及错误输出意味着改变文件描述符0,1,2的 15 | 连接.有很多种技术来重定向标准I/O 16 | 17 | 管道是内核中的一个数据队列,其每一端连接一个文件描述符.程序通过 18 | 使用pipe系统调用创建管道 19 | 20 | 当父进程调用fork的时候,管道的两端都被复制到子进程中 21 | 22 | 只有有共同父进程的进程之间才可以使用管道连接 23 | 24 | 两个进程都可以读写管道,但是当一个进程读,另一个进程写的时候,管道的使用效率最高 25 | -------------------------------------------------------------------------------- /00.book/chapter11_socket: -------------------------------------------------------------------------------- 1 | 第11章 连接到近端或远端的进程:服务器与Socket(套接字) 2 | 3 | 一些程序被作为单独的进程建立起来来接受和发送数据.在客户/服务器模型中, 4 | 服务器进程为客户进程提供处理或数据服务 5 | 6 | 客户/服务器系统包含通信系统和协议.客户和服务器通过管道或socket进行通信. 7 | 协议是会话过程中一系列规则的集合 8 | 9 | popen库函数可以将任何shell程序嵌入服务器程序并且让对服务器的访问就像访问 10 | 缓存文件一样 11 | 12 | 管道是一对相连接的文件描述符.socket是一个未连接的通信端点,也是一个潜在 13 | 的文件描述符.客户进程通过把自己的socket和服务器端的socket相连来创建一个 14 | 通信连接 15 | 16 | sockets之间的连接可以扩展到另一台机器上.每个socket以机器地址和端口来标识 17 | 18 | 到管道和socket的连接使用文件描述符.文件描述符为程序提供了与文件,设备和 19 | 其他的进程通信的统一编程接口 20 | 21 | Unix中的计算器:bc 22 | bc在内部启动了dc计算器程序,并通过管道与其进行通信 23 | 24 | 从bc方法中得到的思想: 25 | 1 客户/服务器模型 26 | bc/dc程序对是客户/服务器模型程序设计的一个实例.bc通过用户界面, 27 | 并使用dc提供的服务.这里bc被称为dc的客户 28 | 2 双向通信 29 | 客户/服务器模型不同于生产线的数据处理模型,他要求一个进程既跟另 30 | 一个进程的标准输入也要和他的标准输出进行通信 31 | 3 永久性服务 32 | bc让单一的dc进程处于运行状态,也就是bc不断的与dc的同一个实例进行 33 | 通信,而shell对每一个命令都创建一个新的进程 34 | bc/dc对被称之为协同进程(coroutines)以用来区别于子程序(subroutines) 35 | 两个程序都持续运行,当其中的一个程序完成自己的工作后将把控制权传给 36 | 另一个程序 37 | 38 | bc的流程: 39 | 1 创建两个管道 40 | 2 创建一个进程来运行dc 41 | 3 在新创建的进程中,重定向标准输入和标准输出到管道,然后运行exec dc 42 | 4 在父进程中,读取并分析用户的输入,将命令传给dc,dc读取响应,并把 43 | 响应传给用户 44 | 45 | 如果知道文件名,可以用fopen打开设备文件 46 | 如果只知道文件描述符,可以用fdopen命令:W 47 | 48 | fopen打开一个指向文件的带缓冲的连接 49 | FILE * fp; 50 | fp = fopen("file", "r"); 51 | c = getc(fp); 52 | fgets(buf, len, fp); 53 | fscanf(fp, "%d%d%s", &x, &y, &z); 54 | fclose(fp); 55 | 56 | popen打开一个指向进程的带缓冲的连接 57 | FILE * fp; 58 | fp = popen("ls", "r"); 59 | fgets(buf, len, fp); 60 | pclose(fp); 61 | 62 | 如果不关闭,进程会变成僵尸进程.pclose中调用了wait函数等待进程的结束 63 | 64 | 访问数据:文件,应用程序接口,服务器 65 | 文件 66 | 依赖于特定的文件格式和结构体中特定的成员名称 67 | 函数 68 | 就算底层存储结构改变,接口程序依然可用 69 | 进程 70 | 使用进程,也就是调用独立的程序来获取数据,而不是自己写的程序 71 | 72 | 73 | 管道使得进程对其他进程发送数据就像向文件发送数据一样容易,但是因为管道 74 | 在进程中被创建,通过fork来实现共享,所以管道只能连接位于同一台主机上的 75 | 进程,而要与远端进程连接,需要使用socket 76 | 77 | 客户和服务器 78 | 服务器是提供服务的程序,是一个进程,等待请求,处理请求,然后循环回去 79 | 等下一个请求.客户端进程只要建立连接,与服务器交换数据即可 80 | 81 | 主机名和端口 82 | 运行于因特网上的服务器其实是某台计算器上运行的一个进程.服务器在该 83 | 主机拥有一个端口.主机和端口的组合才标识了一个服务器 84 | 85 | 协议 86 | 协议是服务器和客户之间交互的规则.每个客户/服务器模型都必须定义一个 87 | 协议并遵守,只要两者相互认可就行,比如约定数据的格式等 88 | 89 | /etc/services中定义了常用的服务器端口列表 90 | 91 | socket中服务器流程: 92 | 1 向内核申请一个socket 93 | socket是一个通信端点 系统调用socket创建一个socket 94 | 2 绑定地址到socket上,地址包括主机,端口 95 | bind调用把一个地址分配给socket 96 | 3 在socket上,允许接入呼叫并设置队列长度 97 | 使用listen 监听端口 98 | 4 等待/接收呼叫 99 | 使用accept来接收调用,accept阻塞当前进程,直到指定socket上的接入连接 100 | 被建立起来,然后返回文件描述符进行读写操作 101 | 5 传输数据 102 | 6 关闭连接 103 | close系统调用 104 | 105 | socket中客户端流程: 106 | 1 向内核申请建立socket 107 | 2 与服务器连接 108 | connect系统调用 109 | 3 传送数据 110 | 4 关闭连接 111 | 112 | 对于任何运行参数中所含的命令或从因特网上获取数据的服务器,在编写时都要格外 113 | 小心,比如收到用户参数里有";rm *" 114 | 115 | -------------------------------------------------------------------------------- /00.book/chapter12_web: -------------------------------------------------------------------------------- 1 | 第12章 连接和协议:编写web服务器 2 | 3 | 基于socket的客户/服务器程序遵循一个标准架构.服务器接收和处理请求,客户 4 | 发出请求 5 | 6 | 服务器建立服务器端socket.服务器端socket有具体的地址,用来接收连接 7 | 8 | 客户创建和使用客户端socket.客户并不关心客户端socket的地址 9 | 10 | 服务器可以用两种方法之一处理请求:自己处理,或fork创建新进程处理请求 11 | 12 | Web服务器是最受欢迎的基于socket的程序.Web服务器处理3种类型的请求: 13 | 返回文件内容,目录列表和运行程序.请求和应答协议称为HTTP 14 | 15 | 服务器的设计问题:DIY或代理 16 | DIY:服务器接收请求,自己处理工作.用于快速简单的任务 17 | 代理:服务器接收请求,然后创建一个新进程处理工作.用于慢速复杂的任务 18 | 19 | Web服务器协议: 20 | HTTP请求:GET 21 | telnet创建socket并调用connect来连接到Web服务器. 22 | HTTP请求:GET /index.html HTTP/1.0 23 | 三部分分别是:命令,参数,协议版本号 24 | 25 | HTTP应答:OK 26 | 服务器读取请求,检查请求,然后返回一个请求.应答包含头部和内容两部分. 27 | 头部:HTTP/1.1 200 OK 28 | 三部分分别是:协议版本号,返回码,文本解释 29 | 内容是具体的网页内容 30 | -------------------------------------------------------------------------------- /00.book/chapter13_datagram: -------------------------------------------------------------------------------- 1 | 第13章 基于数据报(Datagram)的编程:编写许可证服务器 2 | 3 | 数据报是从一个socket发送到另一个socket的短消息.数据报socket是不连接的, 4 | 每个消息包含有目的地址.数据报(UDP)socket更加简单,快速,给系统增加的负荷 5 | 更小. 6 | 7 | 许可证服务器是用来对被许可程序实施许可证验证规则的,许可证服务器发布许可, 8 | 以短消息的形式发送给客户. 9 | 10 | 许可证服务器必须记住哪个进程使用了哪个票据,必须维持一个内部的数据库.因此, 11 | 许可证服务器不同于简单的服务器. 12 | 13 | 记录系统状态的服务器必须设计成可以处理服务器和客户端的崩溃事件. 14 | 15 | 有些许可证服务器为一个网络上的多个机器提供服务.有几种设计方法,各有优缺点. 16 | 17 | socket可以有两种类型的地址:网络或者本地.本地的socket地址叫做Unix域socket 18 | 或名字socket.这种socket使用文件名作为地址,只能在一台机器上交互数据. 19 | -------------------------------------------------------------------------------- /00.book/chapter14_thread: -------------------------------------------------------------------------------- 1 | 第14章 线程机制:并发函数的使用 2 | 3 | 执行线路即为程序的控制流程.pthreads的线程库允许程序在同一时刻运行多个函数 4 | 5 | 同时执行的各函数都拥有自己的局部变量,但共享所有的全局变量和动态分配的数据空间 6 | 7 | 当线程共享变量时,必须保证他们不会发生共享冲突.线程使用互斥锁保证在某一时刻只有 8 | 一个线程在对共享变量访问 9 | 10 | 线程间通过条件变量来互相通知和同步数据.一个线程挂起并等待着条件变量按照某种特定 11 | 方式变化,而另一个线程则发出信号使得条件变量发生变化 12 | 13 | 线程需要使用互斥量来避免对于共享资源操作函数的访问冲突.非重入的函数必须按照 14 | 这种方式进行保护 15 | 16 | 进程间可以通过管道 socket 信号 退出/等待以及运行环境来进行会话.线程因为是在 17 | 一个单独的进程中运行,共享全局变量,因此线程可以通过设置和读取这些全局变量来 18 | 进行通信,对共享内存的访问,既有用也危险 19 | 20 | 21 | -------------------------------------------------------------------------------- /00.book/chapter15_IPC: -------------------------------------------------------------------------------- 1 | 第15章 进程间通信(IPC) 2 | 3 | 许多程序都包含一个或多个进程.进程间通过共享数据或传递数据进行通信.举例来说, 4 | 两个人通过使用Unix的talk命令进行对话,他们就运行了两个进程,将数据从键盘和socket 5 | 传输到屏幕和socket 6 | 7 | 某些进程需要从多个源端接受数据,并将数据送到多个目的地.select和poll调用允许进程 8 | 等待多个文件描述符的输入 9 | 10 | Unix提供了许多方法来进行数据在进程间传输,命名管道和共享内存是同一机器上的进程间 11 | 通信使用的两种技术.通信方法的区别在于他们的速度,所传输的消息类型,需要的范围, 12 | 限制访问权限的能力以及防止数据冲突的能力 13 | 14 | 文件锁是进程间使用的避免对文件访问冲突的技术 15 | 16 | 信号量是进程合作时所使用的系统级的变量.进程挂起等待另一进程改变信号量的值 17 | 18 | 纵观IPC: 19 | fork-execv-argv, exit-wait 20 | 用于使用一组参数来调用某个程序,被调用函数将一个整形值返回给其调用者 21 | 面向消息,只能用在相关的进程中,且只能单机上使用 22 | 23 | environ 24 | 系统调用exec通过environ这个全局变量自动将一组字符串复制给新程序,子进程无法改变 25 | 父进程的运行环境 26 | 面向对象,单向,只能用在相关进程,且只能在单机上使用 27 | 28 | pipe 29 | 管道是由进程创建的单向数据流 30 | 面向流,单向,相关进程,单机 31 | 32 | kill-signal 33 | 信号是一条从一个进程发往另一个进程的整形消息 34 | 面向消息,单向,进程必须有相同的用户ID,单机 35 | 36 | Internet sockets 37 | 字节流通过socket进行传输,从一个进程到另一个进程 38 | 面向消息,面向流,双向传输,可以在无关进程中使用,可通过网络传输 39 | 40 | Named Sockets 41 | 命名socket,使用文件名作为地址而不是主机名-端口号对 42 | 面向消息,面向流,双向传输,可以在无关进程中使用,单机 43 | 44 | Named Pipes(FIFOs) 45 | 命令管道工作方式类似于常规管道,但是可以连接两个无关进程 46 | 单向,面向流,可以连接无关进程,单机 47 | 48 | File Locks 49 | 文件锁 50 | 面向消息,多个无关进程可同时交互,单机 51 | 52 | Shared Memory 53 | 每个进程有自己的数据空间,进程可以通过shmget shmat调用来创建可以被多个进程共享的内存段 54 | 面向随即访问,多个无关进程同时交互,单机 55 | 56 | Semaphores 57 | 信号量是系统级的变量,程序之间可以通过信号量进行通信 58 | 面向消息,多个无关进程同时交互,单机 59 | 60 | Message Queues 61 | 消息队列工作原理类似FIFO 62 | 面向消息,单向传输,单机 63 | 64 | Files 65 | 文件可以被多个进程在同一时刻打开 66 | 面向随即访问,多个无关进程同时交互,网络文件系统(NFS)可以支持跨机器的多进程通信 67 | -------------------------------------------------------------------------------- /01.more/more01: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/01.more/more01 -------------------------------------------------------------------------------- /01.more/more01.c: -------------------------------------------------------------------------------- 1 | /* filename: more01.c 2 | * read and print 24 lines then pause for a few special commands 3 | */ 4 | 5 | #include 6 | 7 | #define PAGELEN 24 8 | #define LINELEN 512 9 | 10 | void do_more(FILE *); 11 | int see_more(); 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | FILE * fp; 16 | if (argc == 1) 17 | do_more(stdin); 18 | else 19 | while(--argc) 20 | if ((fp = fopen(* ++argv, "r")) != NULL) 21 | { 22 | do_more(fp); 23 | fclose(fp); 24 | } 25 | else 26 | exit(1); 27 | return 0; 28 | } 29 | 30 | void do_more(FILE * fp) 31 | { 32 | char line[LINELEN]; 33 | int num_of_lines = 0; 34 | int see_more(), reply; 35 | 36 | while(fgets(line, LINELEN, fp)) // more input 37 | { 38 | if (num_of_lines == PAGELEN) // full screen? 39 | { 40 | reply = see_more(); // y: ask user 41 | if (reply == 0) // n: done 42 | break; 43 | num_of_lines -= reply; // reset count 44 | } 45 | if (fputs(line, stdout) == EOF) // show line 46 | exit(1); // or die 47 | num_of_lines++; // count it 48 | } 49 | } 50 | 51 | int see_more() 52 | { 53 | int c; 54 | printf("\033[7m more?\033[m"); // reverse on a vt100 55 | while ((c = getchar()) != EOF) // get response 56 | { 57 | if (c == 'q') // a -> N 58 | return 0; 59 | if (c == ' ') // ' ' -> next page 60 | return PAGELEN; // how many to show 61 | if (c == '\n') // Enter key -> 1 line 62 | return 1; 63 | } 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /01.more/more02: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/01.more/more02 -------------------------------------------------------------------------------- /01.more/more02.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PAGELEN 24 4 | #define LINELEN 512 5 | 6 | void do_more(FILE *); 7 | int see_more(FILE *); 8 | 9 | int main(int argc, char * argv[]) 10 | { 11 | FILE * fp; 12 | if (argc == 1) 13 | do_more(stdin); 14 | else 15 | while(--argc) 16 | if ((fp = fopen(* ++argv, "r")) != NULL) 17 | { 18 | do_more(fp); 19 | fclose(fp); 20 | } 21 | else 22 | exit(1); 23 | return 0; 24 | } 25 | 26 | void do_more(FILE * fp) 27 | { 28 | char line[LINELEN]; 29 | int num_of_lines = 0; 30 | int see_more(FILE *), reply; 31 | FILE * fp_tty; 32 | fp_tty = fopen("/dev/tty", "r"); 33 | if (fp_tty == NULL) 34 | exit(1); 35 | while(fgets(line, LINELEN, fp)) 36 | { 37 | if (num_of_lines == PAGELEN) 38 | { 39 | reply = see_more(fp_tty); 40 | if (reply == 0) 41 | break; 42 | num_of_lines -= reply; 43 | } 44 | if (fputs(line, stdout) == EOF) 45 | exit(1); 46 | num_of_lines++; 47 | } 48 | } 49 | 50 | int see_more(FILE * cmd) 51 | { 52 | int c; 53 | printf("\033[7m more? \033[m"); 54 | while ((c = getc(cmd)) != EOF) 55 | { 56 | if (c == 'q') 57 | return 0; 58 | if (c == ' ') 59 | return PAGELEN; 60 | if (c == '\n') 61 | return 1; 62 | } 63 | return 0; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /02.who/cp1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/02.who/cp1 -------------------------------------------------------------------------------- /02.who/cp1.c: -------------------------------------------------------------------------------- 1 | /* cp1.c 2 | * version 1 of cp - uses read and write with tunable buffer size 3 | * 4 | * usage: cp1 src dest 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define BUFFERSIZE 4096 12 | #define COPYMODE 0644 13 | 14 | void oops(char *, char *); 15 | 16 | int main(int ac, char * av[]) 17 | { 18 | int in_fd, out_fd, n_chars; 19 | char buf[BUFFERSIZE]; 20 | 21 | if (ac != 3) 22 | { 23 | printf(stderr, "usage: %s source destination\n", av); 24 | exit(1); 25 | } 26 | 27 | if ((in_fd = open(av[1], O_RDONLY)) == -1) 28 | oops("Cannot open ", av[1]); 29 | 30 | if ((out_fd = creat(av[2], COPYMODE)) == -1) 31 | oops("Cannot create ", av[2]); 32 | 33 | while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0) // n_chars size 34 | if (write(out_fd, buf, n_chars) != n_chars) 35 | oops("Write error to ", av[2]); 36 | 37 | if (n_chars == -1) 38 | oops("Read error from ", av[1]); 39 | 40 | if (close(in_fd) == -1 || close(out_fd) == -1) 41 | oops("Error closing files", ""); 42 | } 43 | 44 | void oops(char * s1, char * s2) 45 | { 46 | fprintf(stderr, "Error: %s", s1); 47 | perror(s2); 48 | exit(1); 49 | } 50 | -------------------------------------------------------------------------------- /02.who/logout_tty.c: -------------------------------------------------------------------------------- 1 | /* logout_tty.c 2 | * marks a utmp reocord as logged out 3 | * does not blank username or remote host 4 | * return -1 on error, 0 on sucess 5 | */ 6 | 7 | int logout_tty(char * line) 8 | { 9 | int fd; 10 | struct utmp rec; 11 | int len = sizeof(rec); 12 | int retval = -1; // pessimism 13 | 14 | if ((fd = open(UTMP_FILE, O_RDWR)) == -1) 15 | return -1; 16 | 17 | // search and replace 18 | while (read(fd, &rec, len) == len) 19 | if (strncmp(rec.ut_line, line, sizeof(rec.ut_line)) == 0) 20 | { 21 | rec.ut_type = DEAD_PROCESS; // set type 22 | if (time(&rec.ut_time != -1)) 23 | if (lseek(fd, -len, SEEK_CUR) != -1) // back up 24 | if (write(fd, &rec, len) == len) // update 25 | retval = 0; // sucess 26 | break; 27 | } 28 | 29 | // close the file 30 | if (close(fd) == -1) 31 | retval = -1; 32 | return retval; 33 | } 34 | -------------------------------------------------------------------------------- /02.who/utmplib.c: -------------------------------------------------------------------------------- 1 | /* utmplib.c - functions to buffer reads from utmp file 2 | * 3 | * reads NRECS per read and then doles them out from the buffer 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define NRECS 16 12 | #define NULLUT ((struct utmp *)NULL) 13 | #define UTSIZE (sizeof(struct utmp)) 14 | 15 | static char utmpbuf[NRECS * UTSIZE]; // storage 16 | static int num_recs; // num stored 17 | static int cur_rec; // num has been used 18 | static int fd_utmp = -1; // read from 19 | 20 | utmp_open(char * filename) 21 | { 22 | fd_utmp = open(filename, O_RDONLY); 23 | cur_rec = num_recs = 0; 24 | return fd_utmp; 25 | } 26 | 27 | struct utmp * utmp_next() 28 | { 29 | struct utmp * recp; 30 | if (fd_utmp == -1) // error ? 31 | return NULLUT; 32 | 33 | if (cur_rec == num_recs && utmp_reload() == 0) // any more 34 | return NULLUT; 35 | 36 | recp = (struct utmp *)&utmpbuf[cur_rec * UTSIZE]; 37 | cur_rec ++; 38 | return recp; 39 | } 40 | 41 | int utmp_reload() 42 | /* 43 | * read next bunch of records into buffer 44 | */ 45 | { 46 | int amt_read; 47 | amt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE); // read them in 48 | num_recs = amt_read / UTSIZE; // how many did we get? 49 | cur_rec = 0; // reset points 50 | return num_recs; 51 | } 52 | 53 | utmp_close() 54 | { 55 | if (fd_utmp != -1) 56 | close(fd_utmp); 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /02.who/who1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/02.who/who1 -------------------------------------------------------------------------------- /02.who/who1.c: -------------------------------------------------------------------------------- 1 | /* who1.c 2 | * a first version of the who program 3 | * open, read UTMP file, and show results 4 | */ 5 | 6 | #include 7 | #include // define the structure which readed 8 | #include // function of open 9 | #include // read, close 10 | 11 | #define SHOWHOST // include remote machine on output 12 | 13 | void show_info(struct utmp * utbufp); 14 | 15 | int main() 16 | { 17 | struct utmp current_record; // read info into here 18 | int utmpfd; // read from this descriptor 19 | int reclen = sizeof(current_record); 20 | 21 | if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1) 22 | { 23 | perror(UTMP_FILE); // UTMP_FILE is in utmp.h 24 | exit(1); 25 | } 26 | 27 | while (read(utmpfd, ¤t_record, reclen) == reclen) 28 | { 29 | // printf("%d %d\n", reclen, utmpfd); 30 | show_info(¤t_record); 31 | } 32 | close(utmpfd); 33 | return 0; // went ok 34 | } 35 | 36 | /* 37 | * show_info() 38 | * display contents of the utmp struct in human readable form 39 | * note: these sizes should not be hardwired 40 | */ 41 | void show_info(struct utmp * utbufp) 42 | { 43 | printf("% -8.8s", utbufp->ut_user); // logname 44 | printf(" "); 45 | printf("% -8.8s", utbufp->ut_line); // tty 46 | printf(" "); 47 | printf("% -8.8ld", utbufp->ut_tv); // login time 48 | printf(" "); 49 | #ifdef SHOWHOST 50 | printf("(%s)", utbufp->ut_host); // host 51 | #endif 52 | printf("\n"); 53 | } 54 | -------------------------------------------------------------------------------- /02.who/who2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/02.who/who2 -------------------------------------------------------------------------------- /02.who/who2.c: -------------------------------------------------------------------------------- 1 | /* who2.c 2 | * a second version of the who program 3 | * open, read UTMP file, and show results 4 | * and filter some white record 5 | */ 6 | 7 | #include 8 | #include // define the structure which readed 9 | #include // function of open 10 | #include // read, close 11 | #include 12 | 13 | // #define SHOWHOST // include remote machine on output 14 | 15 | void show_time(long); 16 | void show_info(struct utmp * utbufp); 17 | 18 | int main() 19 | { 20 | struct utmp current_record; // read info into here 21 | int utmpfd; // read from this descriptor 22 | int reclen = sizeof(current_record); 23 | 24 | if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1) 25 | { 26 | perror(UTMP_FILE); // UTMP_FILE is in utmp.h 27 | exit(1); 28 | } 29 | 30 | while (read(utmpfd, ¤t_record, reclen) == reclen) 31 | { 32 | // printf("%d %d\n", reclen, utmpfd); 33 | show_info(¤t_record); 34 | } 35 | close(utmpfd); 36 | return 0; // went ok 37 | } 38 | 39 | /* 40 | * show_info() 41 | * display contents of the utmp struct in human readable form 42 | * note: these sizes should not be hardwired 43 | */ 44 | void show_info(struct utmp * utbufp) 45 | { 46 | if (utbufp->ut_type != USER_PROCESS) 47 | return; 48 | 49 | printf("% -8.8s", utbufp->ut_user); // logname 50 | printf(" "); 51 | printf("% -8.8s", utbufp->ut_line); // tty 52 | printf(" "); 53 | show_time(utbufp->ut_tv.tv_sec); 54 | #ifdef SHOWHOST 55 | if (utbufp->ut_host[0] != '\0') 56 | printf("(%s)", utbufp->ut_host); // host 57 | #endif 58 | printf("\n"); 59 | } 60 | 61 | /* 62 | * display time in a format fit for human consumption 63 | * uses ctime to build a string then picks parts out of it 64 | * Note: %12.12s prints a string 12 chars wid and LIMTS 65 | * it to 12 chars. 66 | */ 67 | void show_time(long timeval) 68 | { 69 | // char * cp; // to hold address of time 70 | // cp = ctime(&timeval); // convert time to string 71 | // string looks like 72 | // Mon Feb 4 00:46:40 EST 1991 73 | // 01234567938272 74 | // printf("%12.12s", cp+4);// pick 12 chars from pos 4 75 | struct tm * cp = localtime(&timeval); 76 | printf("%d-%d-%d %d:%d", cp->tm_year+1900, cp->tm_mon+1, cp->tm_mday, 77 | cp->tm_hour, cp->tm_min);// pick 12 chars from pos 4 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /02.who/who3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/02.who/who3 -------------------------------------------------------------------------------- /02.who/who3.c: -------------------------------------------------------------------------------- 1 | /* who3.c 2 | * a second version of the who program 3 | * open, read UTMP file, and show results 4 | * and filter some white record 5 | * and add buffter to fast the execute 6 | */ 7 | 8 | #include 9 | #include // define the structure which readed 10 | #include // function of open 11 | #include // read, close 12 | #include 13 | 14 | // #define SHOWHOST // include remote machine on output 15 | 16 | void show_time(long); 17 | void show_info(struct utmp * utbufp); 18 | 19 | int main() 20 | { 21 | struct utmp * utbufp, // holds pointer to next rec 22 | * utmp_nex(); // returns pointer to next 23 | 24 | if (utmp_open(UTMP_FILE) == -1) 25 | { 26 | perror(UTMP_FILE); // UTMP_FILE is in utmp.h 27 | exit(1); 28 | } 29 | 30 | while ((utbufp = utmp_next()) != ((struct utmp *)NULL)) 31 | { 32 | // printf("%d %d\n", reclen, utmpfd); 33 | show_info(utbufp); 34 | } 35 | utmp_close(); 36 | return 0; // went ok 37 | } 38 | 39 | /* 40 | * show_info() 41 | * display contents of the utmp struct in human readable form 42 | * note: these sizes should not be hardwired 43 | */ 44 | void show_info(struct utmp * utbufp) 45 | { 46 | if (utbufp->ut_type != USER_PROCESS) 47 | return; 48 | 49 | printf("% -8.8s", utbufp->ut_user); // logname 50 | printf(" "); 51 | printf("% -8.8s", utbufp->ut_line); // tty 52 | printf(" "); 53 | show_time(utbufp->ut_tv.tv_sec); 54 | #ifdef SHOWHOST 55 | if (utbufp->ut_host[0] != '\0') 56 | printf("(%s)", utbufp->ut_host); // host 57 | #endif 58 | printf("\n"); 59 | } 60 | 61 | /* 62 | * display time in a format fit for human consumption 63 | * uses ctime to build a string then picks parts out of it 64 | * Note: %12.12s prints a string 12 chars wid and LIMTS 65 | * it to 12 chars. 66 | */ 67 | void show_time(long timeval) 68 | { 69 | // char * cp; // to hold address of time 70 | // cp = ctime(&timeval); // convert time to string 71 | // string looks like 72 | // Mon Feb 4 00:46:40 EST 1991 73 | // 01234567938272 74 | // printf("%12.12s", cp+4);// pick 12 chars from pos 4 75 | struct tm * cp = localtime(&timeval); 76 | printf("%d-%d-%d %d:%d", cp->tm_year+1900, cp->tm_mon+1, cp->tm_mday, 77 | cp->tm_hour, cp->tm_min);// pick 12 chars from pos 4 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /03.ls/ls1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/03.ls/ls1 -------------------------------------------------------------------------------- /03.ls/ls1.c: -------------------------------------------------------------------------------- 1 | /* ls1.c 2 | * purpose list contents of directory or directories 3 | * action if no args, use . else list files in args 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void do_ls(char []); 11 | 12 | main(int ac, char * av[]) 13 | { 14 | if (ac == 1) 15 | do_ls("."); 16 | else 17 | while (--ac) 18 | { 19 | printf("%s:\n", * ++av); 20 | do_ls(*av); 21 | } 22 | } 23 | 24 | void do_ls(char dirname[]) 25 | /* 26 | * list files in directory called dirname 27 | */ 28 | { 29 | DIR *dir_ptr; // the directory 30 | struct dirent * direntp;// each entry 31 | 32 | if ((dir_ptr = opendir(dirname)) == NULL) 33 | fprintf(stderr, "ls1: cannot open %s\n", dirname); 34 | else 35 | { 36 | while ((direntp = readdir(dir_ptr)) != NULL) 37 | printf("%s\n", direntp->d_name); 38 | closedir(dir_ptr); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /03.ls/ls2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/03.ls/ls2 -------------------------------------------------------------------------------- /03.ls/ls2.c: -------------------------------------------------------------------------------- 1 | /* ls2.c 2 | * purpose list contents of directory or directories 3 | * action if no args, use . else list files in args 4 | * note uses stat and pwd.h and grp.h 5 | * BUG: try ls2 /tmp 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void do_ls(char []); 14 | void dostat(char *); 15 | void show_file_info(char *, struct stat *); 16 | void mode_to_letters(int , char []); 17 | char * uid_to_name(uid_t); 18 | char * gid_to_name(gid_t); 19 | 20 | main(int ac, char * av[]) 21 | { 22 | if (ac == 1) 23 | do_ls("."); 24 | else 25 | while (--ac) 26 | { 27 | printf("%s:\n", * ++av); 28 | do_ls(*av); 29 | } 30 | } 31 | 32 | void do_ls(char dirname[]) 33 | /* 34 | * list files in directory called dirname 35 | */ 36 | { 37 | DIR *dir_ptr; // the directory 38 | struct dirent * direntp;// each entry 39 | 40 | if ((dir_ptr = opendir(dirname)) == NULL) 41 | fprintf(stderr, "ls1: cannot open %s\n", dirname); 42 | else 43 | { 44 | while ((direntp = readdir(dir_ptr)) != NULL) 45 | dostat(direntp->d_name); 46 | closedir(dir_ptr); 47 | } 48 | } 49 | 50 | void dostat(char *filename) 51 | { 52 | struct stat info; 53 | if (stat(filename, &info) == -1) // cannot stat 54 | perror(filename); // say why 55 | else 56 | show_file_info(filename, &info); 57 | } 58 | 59 | void show_file_info(char *filename, struct stat * info_p) 60 | /* 61 | * display the info about filename. 62 | * the info is stored in struct at * info_p 63 | */ 64 | { 65 | char * uid_to_name(), *ctime(), *gid_to_name(), *filemode(); 66 | void mode_to_letters(); 67 | char modestr[11]; 68 | 69 | mode_to_letters(info_p->st_mode, modestr); 70 | 71 | printf("%s", modestr); 72 | printf("%4d ", (int)info_p->st_nlink); 73 | printf("%-8s ", uid_to_name(info_p->st_uid)); 74 | printf("%-8s ", gid_to_name(info_p->st_gid)); 75 | printf("%8ld ", (long)info_p->st_size); 76 | printf("%.12s ", 4+ctime(&info_p->st_mtime)); 77 | printf("%s\n", filename); 78 | } 79 | 80 | /* 81 | * utility functions 82 | */ 83 | 84 | /* 85 | * This function takes a mode value and a char array 86 | * and puts into the char array the file type and the 87 | * nine letters that correspnd to the bits in mode. 88 | * NOTE: It does not code setuid, setgid, and sticky 89 | * codes 90 | */ 91 | void mode_to_letters(int mode, char str[]) 92 | { 93 | strcpy(str, "----------"); // default = no perms 94 | if (S_ISDIR(mode)) str[0] = 'd'; // directory 95 | if (S_ISCHR(mode)) str[0] = 'c'; // char devices 96 | if (S_ISBLK(mode)) str[0] = 'l'; // block device 97 | 98 | if (mode & S_IRUSR) str[1] = 'r'; // 3 bits for user 99 | if (mode & S_IWUSR) str[2] = 'w'; 100 | if (mode & S_IXUSR) str[3] = 'x'; 101 | 102 | if (mode & S_IRGRP) str[4] = 'r'; // 3 bits for group 103 | if (mode & S_IWGRP) str[5] = 'w'; 104 | if (mode & S_IXGRP) str[6] = 'x'; 105 | 106 | if (mode & S_IROTH) str[7] = 'r'; // 3 bits for other 107 | if (mode & S_IWOTH) str[8] = 'w'; 108 | if (mode & S_IXOTH) str[9] = 'x'; 109 | } 110 | 111 | #include 112 | 113 | char * uid_to_name(uid_t uid) 114 | /* 115 | * returns pointer to username associated with uid, uses getpw() 116 | */ 117 | { 118 | struct passwd * getpwuid(), *pw_ptr; 119 | static char numstr[10]; 120 | 121 | if ((pw_ptr = getpwuid(uid)) == NULL) 122 | { 123 | sprintf(numstr, "%d", uid); 124 | return numstr; 125 | } 126 | else 127 | return pw_ptr->pw_name; 128 | } 129 | 130 | #include 131 | 132 | char * gid_to_name(gid_t gid) 133 | /* 134 | * returns pointer to group number gid, used getgrid(3) 135 | */ 136 | { 137 | struct group * getgrpid(), *grp_ptr; 138 | static char numstr[10]; 139 | 140 | if ((grp_ptr = getgrgid(gid)) == NULL) 141 | { 142 | sprintf(numstr, "%d", gid); 143 | return numstr; 144 | } 145 | else 146 | return grp_ptr->gr_name; 147 | } 148 | 149 | -------------------------------------------------------------------------------- /03.ls/ls3.c: -------------------------------------------------------------------------------- 1 | /* ls2.c 2 | * purpose list contents of directory or directories 3 | * action if no args, use . else list files in args 4 | * note uses stat and pwd.h and grp.h 5 | * BUG: try ls2 /tmp 6 | * add sort the filename 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void do_ls(char []); 15 | void dostat(char *); 16 | void show_file_info(char *, struct stat *); 17 | void mode_to_letters(int , char []); 18 | char * uid_to_name(uid_t); 19 | char * gid_to_name(gid_t); 20 | 21 | void my_sort(char * lst, int length) 22 | 23 | main(int ac, char * av[]) 24 | { 25 | if (ac == 1) 26 | do_ls("."); 27 | else 28 | while (--ac) 29 | { 30 | printf("%s:\n", * ++av); 31 | do_ls(*av); 32 | } 33 | } 34 | 35 | void do_ls(char dirname[]) 36 | /* 37 | * list files in directory called dirname 38 | */ 39 | { 40 | DIR *dir_ptr; // the directory 41 | struct dirent * direntp;// each entry 42 | 43 | if ((dir_ptr = opendir(dirname)) == NULL) 44 | fprintf(stderr, "ls1: cannot open %s\n", dirname); 45 | else 46 | { 47 | // add a simple sort of filename 48 | char array[][20]; 49 | while ((direntp = readdir(dir_ptr)) != NULL) 50 | dostat(direntp->d_name); 51 | closedir(dir_ptr); 52 | } 53 | } 54 | 55 | void dostat(char *filename) 56 | { 57 | struct stat info; 58 | if (stat(filename, &info) == -1) // cannot stat 59 | perror(filename); // say why 60 | else 61 | show_file_info(filename, &info); 62 | } 63 | 64 | void show_file_info(char *filename, struct stat * info_p) 65 | /* 66 | * display the info about filename. 67 | * the info is stored in struct at * info_p 68 | */ 69 | { 70 | char * uid_to_name(), *ctime(), *gid_to_name(), *filemode(); 71 | void mode_to_letters(); 72 | char modestr[11]; 73 | 74 | mode_to_letters(info_p->st_mode, modestr); 75 | 76 | printf("%s", modestr); 77 | printf("%4d ", (int)info_p->st_nlink); 78 | printf("%-8s ", uid_to_name(info_p->st_uid)); 79 | printf("%-8s ", gid_to_name(info_p->st_gid)); 80 | printf("%8ld ", (long)info_p->st_size); 81 | printf("%.12s ", 4+ctime(&info_p->st_mtime)); 82 | printf("%s\n", filename); 83 | } 84 | 85 | /* 86 | * utility functions 87 | */ 88 | 89 | /* 90 | * This function takes a mode value and a char array 91 | * and puts into the char array the file type and the 92 | * nine letters that correspnd to the bits in mode. 93 | * NOTE: It does not code setuid, setgid, and sticky 94 | * codes 95 | */ 96 | void mode_to_letters(int mode, char str[]) 97 | { 98 | strcpy(str, "----------"); // default = no perms 99 | if (S_ISDIR(mode)) str[0] = 'd'; // directory 100 | if (S_ISCHR(mode)) str[0] = 'c'; // char devices 101 | if (S_ISBLK(mode)) str[0] = 'l'; // block device 102 | 103 | if (mode & S_IRUSR) str[1] = 'r'; // 3 bits for user 104 | if (mode & S_IWUSR) str[2] = 'w'; 105 | if (mode & S_IXUSR) str[3] = 'x'; 106 | 107 | if (mode & S_IRGRP) str[4] = 'r'; // 3 bits for group 108 | if (mode & S_IWGRP) str[5] = 'w'; 109 | if (mode & S_IXGRP) str[6] = 'x'; 110 | 111 | if (mode & S_IROTH) str[7] = 'r'; // 3 bits for other 112 | if (mode & S_IWOTH) str[8] = 'w'; 113 | if (mode & S_IXOTH) str[9] = 'x'; 114 | } 115 | 116 | #include 117 | 118 | char * uid_to_name(uid_t uid) 119 | /* 120 | * returns pointer to username associated with uid, uses getpw() 121 | */ 122 | { 123 | struct passwd * getpwuid(), *pw_ptr; 124 | static char numstr[10]; 125 | 126 | if ((pw_ptr = getpwuid(uid)) == NULL) 127 | { 128 | sprintf(numstr, "%d", uid); 129 | return numstr; 130 | } 131 | else 132 | return pw_ptr->pw_name; 133 | } 134 | 135 | #include 136 | 137 | char * gid_to_name(gid_t gid) 138 | /* 139 | * returns pointer to group number gid, used getgrid(3) 140 | */ 141 | { 142 | struct group * getgrpid(), *grp_ptr; 143 | static char numstr[10]; 144 | 145 | if ((grp_ptr = getgrgid(gid)) == NULL) 146 | { 147 | sprintf(numstr, "%d", gid); 148 | return numstr; 149 | } 150 | else 151 | return grp_ptr->gr_name; 152 | } 153 | 154 | 155 | void my_sort(char * lst, int length) 156 | { 157 | for (int i = 0; i < length; ++i) 158 | { 159 | for (int j = i+1; j < length; ++j) 160 | { 161 | // printf("%d %d %d %d\n", i, j, lst[i], lst[j]); 162 | if (lst[i] > lst[j]) 163 | { 164 | char tmp = lst[j]; 165 | lst[j] = lst[i]; 166 | lst[i] = tmp; 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /03.ls/malloc_element: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/03.ls/malloc_element -------------------------------------------------------------------------------- /03.ls/malloc_element.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "string.h" 3 | 4 | main(int ac, char * av[]) 5 | { 6 | // int *p = (int *)malloc(sizeof(int) * 10); 7 | // char (*p)[10] = (char *)malloc(sizeof(char*) * 10); 8 | char *p[10] = {}; 9 | 10 | for (int i = 0; i < 14; ++i) 11 | { 12 | // printf("%d\n", i); 13 | // *p = i; 14 | // p ++; 15 | char s[10] = "abc"; 16 | strcpy(p[i], s); 17 | } 18 | 19 | 20 | // p[0] = 222; 21 | 22 | 23 | 24 | for (int i = 0; i < 12; ++i) 25 | printf("%s\n", p[i]); 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /03.ls/my_sort: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/03.ls/my_sort -------------------------------------------------------------------------------- /03.ls/my_sort.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * my sort 3 | */ 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | void my_sort(int * lst, int length); 10 | void my_sort(string * lst, int length); 11 | void my_sort(char * lst, int length); 12 | 13 | void print_lst(int *lst, int length); 14 | void print_lst(char *lst, int length); 15 | void print_lst(string *lst, int length); 16 | 17 | int main() 18 | { 19 | // int lst[] = {1, 3,2, 5,4}; 20 | // string lst[] = {"lnliu", "fxzhao", "mm"}; 21 | char lst[] = {'l', 'f', 'm'}; 22 | int length = sizeof(lst) / sizeof(lst[0]); 23 | char *sorted_lst = lst; 24 | print_lst(sorted_lst, length); 25 | 26 | my_sort(sorted_lst, length); 27 | 28 | print_lst(sorted_lst, length); 29 | 30 | 31 | return 0; 32 | } 33 | 34 | void my_sort(int * lst, int length) 35 | { 36 | for (int i = 0; i < length; ++i) 37 | { 38 | for (int j = i+1; j < length; ++j) 39 | { 40 | // printf("%d %d %d %d\n", i, j, lst[i], lst[j]); 41 | if (lst[i] > lst[j]) 42 | { 43 | int tmp = lst[j]; 44 | lst[j] = lst[i]; 45 | lst[i] = tmp; 46 | } 47 | } 48 | } 49 | } 50 | 51 | void my_sort(string * lst, int length) 52 | { 53 | for (int i = 0; i < length; ++i) 54 | { 55 | for (int j = i+1; j < length; ++j) 56 | { 57 | // printf("%d %d %d %d\n", i, j, lst[i], lst[j]); 58 | if (lst[i] > lst[j]) 59 | { 60 | string tmp = lst[j]; 61 | lst[j] = lst[i]; 62 | lst[i] = tmp; 63 | } 64 | } 65 | } 66 | } 67 | 68 | void my_sort(char * lst, int length) 69 | { 70 | for (int i = 0; i < length; ++i) 71 | { 72 | for (int j = i+1; j < length; ++j) 73 | { 74 | // printf("%d %d %d %d\n", i, j, lst[i], lst[j]); 75 | if (lst[i] > lst[j]) 76 | { 77 | char tmp = lst[j]; 78 | lst[j] = lst[i]; 79 | lst[i] = tmp; 80 | } 81 | } 82 | } 83 | } 84 | void print_lst(int *lst, int length) 85 | { 86 | for (int i = 0; i < length; ++i) 87 | printf("%d ", lst[i]); 88 | printf("\n"); 89 | } 90 | 91 | void print_lst(string *lst, int length) 92 | { 93 | for (int i = 0; i < length; ++i) 94 | std::cout< 2 | #include 3 | 4 | int comp_int(const void *a, const void *b); 5 | int comp_char(const void *a, const void *b); 6 | int comp_string(const void *a, const void *b); 7 | 8 | int main() 9 | { 10 | // int a[4] = {3, 4, 1, 2}; 11 | // char a[4] = {'b', 'a', 'd', 'c'}; 12 | char a[][20] = {}; 13 | // char a[4][20] = {"bbb", "aaa", "bba", "ccc"}; 14 | 15 | for (int i = 0; i < 4; ++i) 16 | { 17 | printf("%s ", a[i]); 18 | } 19 | printf("\n"); 20 | 21 | qsort(a, 4, sizeof(a[0]), comp_string); 22 | 23 | for (int i = 0; i < 4; ++i) 24 | printf("%s ", a[i]); 25 | printf("\n"); 26 | 27 | return 0; 28 | } 29 | 30 | int comp_int(const void *a, const void *b) 31 | { 32 | return *(int *)a - *(int *)b; 33 | } 34 | 35 | int comp_char(const void *a, const void *b) 36 | { 37 | return *(char *)a - *(char *)b; 38 | } 39 | 40 | int comp_string(const void *a, const void *b) 41 | { 42 | int return_val; 43 | char *t1 = a; 44 | char *t2 = b; 45 | printf("%s %s\n", t1, t2); 46 | 47 | 48 | for (int i = 0; i < strlen(t1), i < strlen(t2); ++i) 49 | { 50 | if (t1[i] == t2[i]) 51 | continue; 52 | else 53 | return t1[i] - t2[i]; 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /03.ls/ttt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/03.ls/ttt -------------------------------------------------------------------------------- /03.ls/ttt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test(void * s); 4 | 5 | int main() 6 | { 7 | /* 8 | char a[10][10] = {'abc', "bac"}; 9 | printf("%s", a[0]); 10 | printf("%s", a[1]); 11 | */ 12 | 13 | char a[10][10] = {"abc", "bac"}; 14 | test(&a[0]); 15 | test(a[1]); 16 | 17 | return 0; 18 | } 19 | 20 | // void test(char * s) 21 | void test(void * s) 22 | { 23 | 24 | char *t = "abc"; 25 | char *t2 = s; 26 | printf("%s\n", s); 27 | printf("%s\n", t); 28 | printf("%c\n", t[0]); 29 | printf("%c\n", t2[0]); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /04.pwd/spwd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/04.pwd/spwd -------------------------------------------------------------------------------- /04.pwd/spwd.c: -------------------------------------------------------------------------------- 1 | /* spwd.c: a simplified version of pwd 2 | * 3 | * starts in current directory and recursively 4 | * climbs up to root of filesystem, prints top part 5 | * then prints current part 6 | * 7 | * uses readdir() to get info about each thing 8 | * 9 | * bug: prints an empty string if run from "/" 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | ino_t get_inode(char *); 18 | void printpathto(ino_t); 19 | void inum_to_name(ino_t, char *, int); 20 | 21 | int ROOT_FLAG = 0; 22 | 23 | int main() 24 | { 25 | printpathto(get_inode(".")); 26 | putchar('\n'); 27 | // printf("\n"); 28 | return 0; 29 | } 30 | 31 | void printpathto(ino_t this_inode) 32 | /* 33 | * prints path leading down to an object with this inode 34 | * kindof recursive 35 | */ 36 | { 37 | ino_t my_node; 38 | char its_name[BUFSIZ]; 39 | if (get_inode("..") != this_inode) 40 | { 41 | ROOT_FLAG = 1; 42 | chdir(".."); 43 | inum_to_name(this_inode, its_name, BUFSIZ); 44 | my_node = get_inode("."); 45 | printpathto(my_node); 46 | printf("/%s", its_name); 47 | } 48 | if (ROOT_FLAG == 0) 49 | printf("/%s", its_name); 50 | 51 | } 52 | 53 | void inum_to_name(ino_t inode_to_find, char * namebuf, int buflen) 54 | /* 55 | * looks through current directory for a file with this inode 56 | * number and copies its name int namebuf 57 | */ 58 | { 59 | DIR * dir_ptr; 60 | struct dirent * direntp; 61 | dir_ptr = opendir("."); 62 | if(dir_ptr == NULL) 63 | { 64 | perror("."); 65 | exit(1); 66 | } 67 | /* 68 | * search directory for a file with specified inum 69 | */ 70 | while((direntp = readdir(dir_ptr)) != NULL) 71 | if (direntp->d_ino == inode_to_find) 72 | { 73 | strncpy(namebuf, direntp->d_name, buflen); 74 | namebuf[buflen-1] = '\0'; 75 | closedir(dir_ptr); 76 | return; 77 | } 78 | fprintf(stderr, "error looking for inum %d\n", inode_to_find); 79 | exit(1); 80 | } 81 | 82 | ino_t get_inode(char * fname) 83 | /* 84 | * returns inode number of the file 85 | */ 86 | { 87 | struct stat info; 88 | if (stat(fname, &info) == -1) 89 | { 90 | fprintf(stderr, "Cannot stat"); 91 | perror(fname); 92 | exit(1); 93 | } 94 | return info.st_ino; 95 | } 96 | -------------------------------------------------------------------------------- /05.stty/echostate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/05.stty/echostate -------------------------------------------------------------------------------- /05.stty/echostate.c: -------------------------------------------------------------------------------- 1 | /* echostate.c 2 | * reports current state of echo bit int tty driver for fd 0 3 | * shows how to read attributes from driver and test a bit 4 | */ 5 | #include 6 | #include 7 | 8 | main() 9 | { 10 | struct termios info; 11 | int rv; 12 | 13 | rv = tcgetattr(0, &info); // read values from driver 14 | if (rv == -1) 15 | { 16 | perror("tcgetattr"); 17 | exit(1); 18 | } 19 | if (info.c_lflag & ECHO) 20 | printf("echo is on, since its bit is 1\n"); 21 | else 22 | printf("echo is off, since its bit is 0\n"); 23 | } 24 | -------------------------------------------------------------------------------- /05.stty/setecho: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/05.stty/setecho -------------------------------------------------------------------------------- /05.stty/setecho.c: -------------------------------------------------------------------------------- 1 | /* setecho.c 2 | * usage: setecho [y|n] 3 | * shows: how to read, change, reset tty attributes 4 | */ 5 | #include 6 | #include 7 | 8 | #define oops(s, x) {perror(s);exit(1);} 9 | 10 | main(int ac, char * av[]) 11 | { 12 | struct termios info; 13 | if (ac == 1) 14 | exit(0); 15 | if (tcgetattr(0, &info) == -1) 16 | oops("tcgetattr", 1); 17 | if (av[1][0] == 'y') 18 | info.c_lflag |= ECHO; 19 | else 20 | info.c_lflag &= ~ECHO; 21 | 22 | if (tcsetattr(0, TCSANOW, &info) == -1) 23 | oops("tcsetattr", 2); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /05.stty/showtty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/05.stty/showtty -------------------------------------------------------------------------------- /05.stty/showtty.c: -------------------------------------------------------------------------------- 1 | /* showtty.c 2 | * display some current tty settings 3 | */ 4 | #include 5 | #include 6 | 7 | main() 8 | { 9 | struct termios ttyinfo; 10 | if (tcgetattr(0, &ttyinfo) == -1) 11 | { 12 | perror("cannot get params about stdin"); 13 | exit(1); 14 | } 15 | 16 | showband(cfgetospeed(&ttyinfo)); 17 | printf("The erase character is ascii %d, Ctrl- %c\n", 18 | ttyinfo.c_cc[VERASE], ttyinfo.c_cc[VERASE]-1+'A'); 19 | printf("The line kill character is ascii %d, Ctrl- %c\n", 20 | ttyinfo.c_cc[VKILL], ttyinfo.c_cc[VKILL]-1+'A'); 21 | show_some_flags(&ttyinfo); 22 | } 23 | 24 | showband(int thespeed) 25 | /* 26 | * prints the speed in english 27 | */ 28 | { 29 | printf("the baud rate is "); 30 | switch(thespeed) 31 | { 32 | case B300: printf("300\n"); break; 33 | case B600: printf("600\n"); break; 34 | case B1200: printf("1200\n"); break; 35 | case B1800: printf("1800\n"); break; 36 | case B2400: printf("2400\n"); break; 37 | case B4800: printf("4800\n"); break; 38 | case B9600: printf("9600\n"); break; 39 | default:printf("Fast\n");break; 40 | } 41 | } 42 | 43 | struct flaginfo {int fl_value; char * fl_name}; 44 | 45 | struct flaginfo input_flags[] = 46 | { 47 | IGNBRK, "Ignore break condition", 48 | BRKINT, "Signal interrupt on break", 49 | IGNPAR, "Ignore chars with parity errors", 50 | PARMRK, "Mark parity errors", 51 | INPCK, "Enable input parity check", 52 | ISTRIP, "Strip character", 53 | INLCR, "Map NL to CR on input", 54 | IGNCR, "Ignore CR", 55 | ICRNL, "Map CR to NL on input", 56 | IXON, "Enable start/stop ouput control", 57 | IXOFF, "Enable start/stop input control", 58 | 0, NULL 59 | }; 60 | 61 | struct flaginfo local_flags[] = 62 | { 63 | ISIG, "Enable signals", 64 | ICANON, "Canonical input(erase and kill)", 65 | ECHO, "Enable echo", 66 | ECHOE, "Echo ERASE as BS-SPACE-BS", 67 | ECHOK, "Echo KILL by starting new line", 68 | 0, NULL 69 | }; 70 | 71 | show_some_flags(struct termios *ttyp) 72 | /* 73 | * show the values of two of the flag sets_:c_iflag and c_lflag 74 | * adding c_oflag and c_cflag is pretty routine - just add new 75 | * tables above and a bit more code below. 76 | */ 77 | { 78 | show_flagset(ttyp->c_iflag, input_flags); 79 | show_flagset(ttyp->c_lflag, local_flags); 80 | } 81 | 82 | show_flagset(int thevalue, struct flaginfo thebitnames[]) 83 | /* 84 | * check each bit pattern and display descriptive title 85 | */ 86 | { 87 | int i; 88 | for (i = 0; thebitnames[i].fl_value; ++i) 89 | { 90 | printf("%s is ", thebitnames[i].fl_name); 91 | if (thevalue & thebitnames[i].fl_value) 92 | printf("ON\n"); 93 | else 94 | printf("OFF\n"); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /05.stty/write0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/05.stty/write0 -------------------------------------------------------------------------------- /05.stty/write0.c: -------------------------------------------------------------------------------- 1 | /* 2 | * write0.c 3 | * 4 | * purpose: send messages to another terminal 5 | * method: open the other teminal for output the 6 | * copy from stdin to that terminal 7 | * shows: a terminal is just a file supporting regular i/o 8 | * usage: write0 ttyname 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | main (int ac, char * av[]) 15 | { 16 | int fd; 17 | char buf[BUFSIZ]; 18 | // check args 19 | if (ac != 2) 20 | { 21 | fprintf(stderr, "usage: write0 ttyname\n"); 22 | exit(1); 23 | } 24 | // open device 25 | fd = open(av[1], O_WRONLY); 26 | if (fd == -1) 27 | { 28 | perror(av[1]); 29 | exit(1); 30 | } 31 | // loop until EOF on input 32 | while (fgets(buf, BUFSIZ, stdin) != NULL) 33 | if (write(fd, buf, strlen(buf)) == -1) 34 | break; 35 | close(fd); 36 | } 37 | -------------------------------------------------------------------------------- /06.signal/play_again0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/play_again0 -------------------------------------------------------------------------------- /06.signal/play_again0.c: -------------------------------------------------------------------------------- 1 | /* 2 | * play_again0.c 3 | * purpose: ask if user wants another transaction 4 | * method: ask a question, wait for yes/no answer 5 | * returns: 0 => yes, 1 => no 6 | * better: eliminate need to press return 7 | */ 8 | #include 9 | #include 10 | 11 | #define QUESTION "Do you want another transaction" 12 | 13 | int get_response(char *); 14 | 15 | int main() 16 | { 17 | int response; 18 | response = get_response(QUESTION); 19 | return response; 20 | } 21 | 22 | int get_response(char * question) 23 | { 24 | printf("%s (y/n)?", question); 25 | while(1) 26 | { 27 | switch(getchar()) 28 | { 29 | case 'y': 30 | case 'Y': return 0; 31 | case 'n': 32 | case 'N': 33 | case EOF: return 1; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /06.signal/play_again1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/play_again1 -------------------------------------------------------------------------------- /06.signal/play_again1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * play_again1.c 3 | * purpose: ask if user wants another transaction 4 | * method: ask a question, wait for yes/no answer 5 | * returns: 0 => yes, 1 => no 6 | * better: do no echo inappropriate input 7 | */ 8 | #include 9 | #include 10 | 11 | #define QUESTION "Do you want another transaction" 12 | 13 | int get_response(char *); 14 | 15 | int main() 16 | { 17 | int response; 18 | tty_mode(0); // save tty mode 19 | set_crmode(); // set chr-by-chr mode 20 | response = get_response(QUESTION); 21 | tty_mode(1); // restore tty mode 22 | return response; 23 | } 24 | 25 | int get_response(char * question) 26 | { 27 | int input; 28 | printf("%s (y/n)?", question); 29 | while(1) 30 | { 31 | switch(input = getchar()) 32 | { 33 | case 'y': 34 | case 'Y': return 0; 35 | case 'n': 36 | case 'N': 37 | case EOF: return 1; 38 | default: 39 | printf("\ncannot understand %c", input); 40 | printf("Please type y or n\n"); 41 | } 42 | } 43 | } 44 | 45 | set_crmode() 46 | /* 47 | * purpose: put file descriptior 0 into chr-by-chr mode 48 | * method: use bits in termios 49 | */ 50 | { 51 | struct termios ttystate; 52 | tcgetattr(0, &ttystate); // read curr. setting 53 | ttystate.c_lflag &= ~ICANON; // no buffering 54 | ttystate.c_cc[VMIN] = 1; // get one char at a time 55 | tcsetattr(0, TCSANOW, &ttystate); // install setting 56 | } 57 | 58 | // how == 0 => save currentmode 59 | // how == 1 => restore mode 60 | tty_mode(int how) 61 | { 62 | static struct termios original_mode; 63 | if (how == 0) 64 | tcgetattr(0, &original_mode); 65 | else 66 | return tcsetattr(0, TCSANOW, &original_mode); 67 | } 68 | -------------------------------------------------------------------------------- /06.signal/play_again2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/play_again2 -------------------------------------------------------------------------------- /06.signal/play_again2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * play_again2.c 3 | * purpose: ask if user wants another transaction 4 | * method: set tty into chr-by-chr mode and no-echo mode 5 | * read char, return result 6 | * returns: 0 => yes, 1 => no 7 | * better: timeout if user walks away 8 | */ 9 | #include 10 | #include 11 | 12 | #define QUESTION "Do you want another transaction" 13 | 14 | int get_response(char *); 15 | 16 | int main() 17 | { 18 | int response; 19 | tty_mode(0); // save tty mode 20 | set_cr_noecho_mode(); // set -icanon, -echo 21 | response = get_response(QUESTION); 22 | tty_mode(1); // restore tty mode 23 | return response; 24 | } 25 | 26 | int get_response(char * question) 27 | { 28 | int input; 29 | printf("%s (y/n)?", question); 30 | while(1) 31 | { 32 | switch(input = getchar()) 33 | { 34 | case 'y': 35 | case 'Y': return 0; 36 | case 'n': 37 | case 'N': 38 | case EOF: return 1; 39 | } 40 | } 41 | } 42 | 43 | set_cr_noecho_mode() 44 | /* 45 | * purpose: put file descriptior 0 into chr-by-chr mode and noecho mode 46 | * method: use bits in termios 47 | */ 48 | { 49 | struct termios ttystate; 50 | tcgetattr(0, &ttystate); // read curr. setting 51 | ttystate.c_lflag &= ~ICANON; // no buffering 52 | ttystate.c_lflag &= ~ECHO; // no echo either 53 | ttystate.c_cc[VMIN] = 1; // get one char at a time 54 | tcsetattr(0, TCSANOW, &ttystate); // install setting 55 | } 56 | 57 | // how == 0 => save currentmode 58 | // how == 1 => restore mode 59 | tty_mode(int how) 60 | { 61 | static struct termios original_mode; 62 | if (how == 0) 63 | tcgetattr(0, &original_mode); 64 | else 65 | return tcsetattr(0, TCSANOW, &original_mode); 66 | } 67 | -------------------------------------------------------------------------------- /06.signal/play_again3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/play_again3 -------------------------------------------------------------------------------- /06.signal/play_again3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * play_again3.c 3 | * purpose: ask if user wants another transaction 4 | * method: set tty into chr-by-chr mode and no-echo mode 5 | * set tty into no-delay mode 6 | * read char, return result 7 | * returns: 0 => yes, 1 => no, 2=> timeout 8 | * better: reset terminal mode on Interrupt 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define ASK "Do you want another transaction" 16 | #define TRIES 3 // max tries 17 | #define SLEEPTIME 2 // time per try 18 | #define BEEP putchar('\a') // alert user 19 | 20 | int main() 21 | { 22 | int response; 23 | tty_mode(0); // save tty mode 24 | set_cr_noecho_mode(); // set -icanon, -echo 25 | set_nodelay_mode(); // noinput => EOF 26 | response = get_response(ASK, TRIES); 27 | tty_mode(1); // restore tty mode 28 | return response; 29 | } 30 | 31 | int get_response(char * question, int maxtries) 32 | { 33 | int input; 34 | printf("%s (y/n)?", question); 35 | fflush(stdout); // force output 36 | while(1) 37 | { 38 | sleep(SLEEPTIME); // wait a bit 39 | input = tolower(get_ok_char()); // get next chr 40 | if (input == 'y') 41 | return 0; 42 | if (input == 'n') 43 | return 1; 44 | if (maxtries-- == 0) 45 | return 2; 46 | BEEP; 47 | } 48 | } 49 | 50 | // skip over non-legal chars and return y,Y,n,N or EOF 51 | get_ok_char() 52 | { 53 | int c; 54 | while ((c = getchar()) != EOF && strchr("yYnN", c) == NULL) 55 | ; 56 | return c; 57 | } 58 | 59 | set_cr_noecho_mode() 60 | /* 61 | * purpose: put file descriptior 0 into chr-by-chr mode and noecho mode 62 | * method: use bits in termios 63 | */ 64 | { 65 | struct termios ttystate; 66 | tcgetattr(0, &ttystate); // read curr. setting 67 | ttystate.c_lflag &= ~ICANON; // no buffering 68 | ttystate.c_lflag &= ~ECHO; // no echo either 69 | ttystate.c_cc[VMIN] = 1; // get one char at a time 70 | tcsetattr(0, TCSANOW, &ttystate); // install setting 71 | } 72 | 73 | set_nodelay_mode() 74 | /* purose: put file descriptor 0 into no-delay mode 75 | * method: use fcntl to set bits 76 | * notes: tcsetattr() will do something similar, but it is complicated 77 | */ 78 | { 79 | int termflags; 80 | termflags = fcntl(0, F_GETFL); // read curr. settings 81 | termflags |= O_NDELAY; // flip on nodelay bit 82 | fcntl(0, F_SETFL, termflags); // and install 'em 83 | } 84 | 85 | // how == 0 => save currentmode 86 | // how == 1 => restore mode 87 | // this version handles termios and fcntl flags 88 | tty_mode(int how) 89 | { 90 | static struct termios original_mode; 91 | static int original_flags; 92 | if (how == 0) 93 | { 94 | tcgetattr(0, &original_mode); 95 | original_flags = fcntl(0, F_GETFL); 96 | } 97 | else 98 | { 99 | tcsetattr(0, TCSANOW, &original_mode); 100 | fcntl(0, F_SETFL, original_flags); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /06.signal/rotate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/rotate -------------------------------------------------------------------------------- /06.signal/rotate.c: -------------------------------------------------------------------------------- 1 | /* rotate.c :map a->b b->c... z->a 2 | * purpose: useful for showing tty mode 3 | */ 4 | #include 5 | #include 6 | int main() 7 | { 8 | int c; 9 | while((c = getchar()) != EOF) 10 | { 11 | if (c == 'z') 12 | c = 'a'; 13 | else if (islower(c)) 14 | c++; 15 | putchar(c); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /06.signal/sigdemo1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/sigdemo1 -------------------------------------------------------------------------------- /06.signal/sigdemo1.c: -------------------------------------------------------------------------------- 1 | /* sigdemo1.c - show how a signal handler works. 2 | * - run this and press Ctrl-C a few times 3 | */ 4 | #include 5 | #include 6 | 7 | main() 8 | { 9 | void f(int); // declare the handler 10 | int i; 11 | signal(SIGINT, f); // install the handler 12 | for (i = 0; i < 5; ++i) 13 | { 14 | printf("hello\n"); 15 | sleep(1); 16 | } 17 | } 18 | 19 | void f(int signum) 20 | { 21 | printf("OUCH! \n"); 22 | } 23 | -------------------------------------------------------------------------------- /06.signal/sigdemo2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/06.signal/sigdemo2 -------------------------------------------------------------------------------- /06.signal/sigdemo2.c: -------------------------------------------------------------------------------- 1 | /* sigdemo2.c - show how to ignore a signal 2 | * - press Ctrl-\ to kill this one 3 | */ 4 | #include 5 | #include 6 | 7 | main() 8 | { 9 | signal(SIGINT, SIG_IGN); 10 | printf("You can't stop me! \n"); 11 | while (1) 12 | { 13 | sleep(1); 14 | printf("haha\n"); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /07.videogame/bounce.h: -------------------------------------------------------------------------------- 1 | /* 2 | * bounce.h 3 | * some settings for the game 4 | */ 5 | #define BLANK ' ' 6 | #define X_BUND '_' 7 | #define XX_BUND '-' 8 | #define Y_BUND '|' 9 | #define DFL_SYMBOL 'o' 10 | #define TOP_ROW 5 11 | #define BOT_ROW 25 12 | #define ERROR 20 13 | #define LEFT_EDGE 10 14 | #define RIGHT_EDGE 70 15 | #define X_INIT 10 16 | #define Y_INIT 10 17 | #define TICKS_PER_SEC 50 18 | 19 | #define X_TTM 5 20 | #define Y_TTM 8 21 | 22 | struct ppball 23 | { 24 | int y_pos, x_pos, 25 | y_ttm, x_ttm, 26 | y_ttg, x_ttg, 27 | y_dir, x_dir; 28 | char symbol; 29 | }; 30 | 31 | struct board 32 | { 33 | int left, row, length, how; 34 | }; 35 | -------------------------------------------------------------------------------- /07.videogame/bounce1d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/bounce1d -------------------------------------------------------------------------------- /07.videogame/bounce1d.c: -------------------------------------------------------------------------------- 1 | /* bounce1d.c 2 | * purpose: animation with user controlled speed and direction 3 | * note: the handler does the animation 4 | * the main program reads keyboard input 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // some global settings main and the handler use 12 | #define MESSAGE "hello" 13 | #define BLANK " " 14 | 15 | int row; 16 | int col; 17 | int dir; 18 | 19 | int main() 20 | { 21 | int delay; 22 | int ndelay; 23 | int c; 24 | void move_msg(int); 25 | 26 | initscr(); 27 | crmode(); 28 | noecho(); 29 | clear(); 30 | 31 | row = 10; 32 | col = 0; 33 | dir = 1; 34 | delay = 200; // 200ms = 0.2seconds 35 | 36 | move(row, col); 37 | addstr(MESSAGE); 38 | signal(SIGALRM, move_msg); 39 | set_ticker(delay); 40 | 41 | while (1) 42 | { 43 | ndelay = 0; 44 | c = getch(); 45 | if (c == 'Q') break; 46 | if (c == ' ') dir = -dir; 47 | if (c == 'f' && delay > 2) ndelay = delay/2; 48 | if (c == 's') ndelay = delay * 2; 49 | if (ndelay > 0) 50 | set_ticker(delay = ndelay); 51 | } 52 | endwin(); 53 | 54 | return 0; 55 | } 56 | 57 | void move_msg(int signum) 58 | { 59 | signal(SIGALRM, move_msg); // reset, just in case 60 | move(row, col); 61 | addstr(BLANK); 62 | col += dir; // move to new column 63 | move(row, col); // then set cursor 64 | addstr(MESSAGE); // redo message 65 | refresh(); 66 | 67 | // now handle borders 68 | if (dir == -1 && col <= 0) 69 | dir = 1; 70 | else if (dir == 1 && col+strlen(MESSAGE) >= COLS) 71 | dir = -1; 72 | } 73 | 74 | /* [from set_ticker.c] 75 | * set_ticker(number_of_milliseconds) 76 | * arranges for interval timer to issue SIGALRMs at regular intervals 77 | * returns -1 on error, 0 for ok 78 | * arg in milliseconds, converted into whole seconds and mocroseconds 79 | * note: set_ticker(0) turns off ticker 80 | */ 81 | int set_ticker(int n_msecs) 82 | { 83 | struct itimerval new_timeset; 84 | long n_sec, n_usecs; 85 | 86 | n_sec = n_msecs / 1000; // int part 87 | n_usecs = (n_msecs % 1000) * 1000L; // remainder 88 | 89 | new_timeset.it_interval.tv_sec = n_sec; // set reload 90 | new_timeset.it_interval.tv_usec = n_usecs; // new ticker value 91 | 92 | new_timeset.it_value.tv_sec = n_sec; // store this 93 | new_timeset.it_value.tv_usec = n_usecs; 94 | 95 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 96 | } 97 | -------------------------------------------------------------------------------- /07.videogame/bounce2d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/bounce2d -------------------------------------------------------------------------------- /07.videogame/bounce2d.c: -------------------------------------------------------------------------------- 1 | /* bounce2d 1.0 2 | * bounce a character around the screen 3 | * defined by some parameters 4 | * 5 | * user input: s slow down x component 6 | * S slow down y component 7 | * f speed up x component 8 | * F speed up y component 9 | * Q quit 10 | * 11 | * blocks on read, but timer tick sends SIGALRM caught by ball_move 12 | * build: cc bounce2d.c -l curses -o bounce2d 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include "bounce.h" 18 | 19 | struct ppball the_ball; 20 | struct board the_board; 21 | 22 | // the main loop 23 | void set_up(); 24 | void wrap_up(); 25 | 26 | int main() 27 | { 28 | FILE *fp; 29 | fp = fopen("result", "w"); 30 | 31 | int c; 32 | set_up(); 33 | while (1) 34 | { 35 | c = getch(); 36 | if (c == 'Q') break; 37 | 38 | fputc(c, fp); 39 | if (c == 'f') the_ball.x_ttm--; 40 | else if (c == 's') the_ball.x_ttm++; 41 | else if (c == 'F') the_ball.y_ttm--; 42 | else if (c == 'S') the_ball.y_ttm++; 43 | else if (c == 'a') 44 | { 45 | if (the_board.left > LEFT_EDGE) 46 | the_board.how = -1; 47 | } 48 | else if (c == 'd') 49 | { 50 | if (the_board.left+the_board.length < RIGHT_EDGE) 51 | the_board.how = 1; 52 | } 53 | else 54 | the_board.how = 0; 55 | 56 | } 57 | wrap_up(); 58 | 59 | fclose(fp); 60 | return 0; 61 | } 62 | 63 | // init structure and other stuff 64 | void set_up() 65 | { 66 | the_board.length = 3; 67 | the_board.left = (RIGHT_EDGE+LEFT_EDGE)/2; 68 | the_board.row = ERROR+1; 69 | the_board.how = 0; 70 | 71 | the_ball.y_pos = Y_INIT; 72 | the_ball.x_pos = X_INIT; 73 | the_ball.y_ttg = the_ball.y_ttm = Y_TTM; 74 | the_ball.x_ttg = the_ball.x_ttm = X_TTM; 75 | the_ball.y_dir = 1; 76 | the_ball.x_dir = 1; 77 | the_ball.symbol = DFL_SYMBOL; 78 | 79 | 80 | initscr(); 81 | noecho(); 82 | crmode(); 83 | 84 | int i; 85 | for (i = LEFT_EDGE-1; i <= RIGHT_EDGE+1; ++i) 86 | { 87 | mvaddch(TOP_ROW-1, i, X_BUND); 88 | //mvaddch(ERROR+1, i, XX_BUND); 89 | //mvaddch(i, TOP_ROW, X_BUND); 90 | //mvaddch(i, BOT_ROW, X_BUND); 91 | } 92 | for (i = TOP_ROW; i <= ERROR+1; ++i) 93 | { 94 | mvaddch(i, LEFT_EDGE-1, Y_BUND); 95 | mvaddch(i, RIGHT_EDGE+1, Y_BUND); 96 | //mvaddch(RIGHT_EDGE, i, Y_BUND); 97 | //mvaddch(LEFT_EDGE, i, Y_BUND); 98 | } 99 | 100 | 101 | for (i = the_board.left; i < the_board.left + the_board.length; ++i) 102 | mvaddch(the_board.row, i, '_'); 103 | 104 | void ball_move(int); 105 | signal(SIGINT, SIG_IGN); 106 | mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 107 | refresh(); 108 | 109 | 110 | signal(SIGALRM, ball_move); 111 | set_ticker(1000/TICKS_PER_SEC); 112 | 113 | } 114 | 115 | void wrap_up() 116 | { 117 | set_ticker(0); 118 | endwin(); 119 | } 120 | 121 | void ball_move(int signum) 122 | { 123 | int y_cur, x_cur, moved; 124 | 125 | signal(SIGALRM, SIG_IGN); // dont get caught now 126 | y_cur = the_ball.y_pos; // old spot 127 | x_cur = the_ball.x_pos; 128 | moved = 0; 129 | 130 | if (the_ball.y_ttm > 0 && the_ball.y_ttg-- == 1) 131 | { 132 | the_ball.y_pos += the_ball.y_dir; // move 133 | the_ball.y_ttg = the_ball.y_ttm; // reset 134 | moved = 1; 135 | } 136 | 137 | if (the_ball.x_ttm > 0 && the_ball.x_ttg-- == 1) 138 | { 139 | the_ball.x_pos += the_ball.x_dir; // move 140 | the_ball.x_ttg = the_ball.x_ttm; // reset 141 | moved = 1; 142 | } 143 | 144 | if (the_board.how != 0) 145 | change_board(the_board.how); 146 | if (moved) 147 | { 148 | mvaddch(y_cur, x_cur, BLANK); 149 | mvaddch(y_cur, x_cur, BLANK); 150 | mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 151 | bounce_or_lose(&the_ball); 152 | move(LINES-1, COLS-1); 153 | refresh(); 154 | } 155 | 156 | signal(SIGALRM, ball_move); // for unreliable systems 157 | } 158 | 159 | 160 | int bounce_or_lose(struct ppball * bp) 161 | { 162 | int return_val = 0; 163 | 164 | if (bp->y_pos == TOP_ROW) 165 | { 166 | bp->y_dir = 1; 167 | return_val = 1; 168 | } 169 | /* 170 | else if (bp->y_pos == BOT_ROW) 171 | { 172 | bp->y_dir = -1; 173 | return_val = 1; 174 | } 175 | */ 176 | else if (bp->y_pos == ERROR + 1) 177 | { 178 | if (bp->x_pos < the_board.left || bp->x_pos > the_board.left + the_board.length) 179 | game_over(); 180 | else 181 | { 182 | bp->y_dir = -1; 183 | return_val = 1; 184 | } 185 | } 186 | 187 | if (bp->x_pos == LEFT_EDGE) 188 | { 189 | bp->x_dir = 1; 190 | return_val = 1; 191 | } 192 | else if (bp->x_pos == RIGHT_EDGE) 193 | { 194 | bp->x_dir = -1; 195 | return_val = 1; 196 | } 197 | return return_val; 198 | } 199 | 200 | void game_over() 201 | { 202 | char * s = "GAME OVER"; 203 | int i, j; 204 | for (i = (LEFT_EDGE+RIGHT_EDGE)/2-4, j = 0; j <= sizeof(s)/sizeof(char); ++i, ++j) 205 | mvaddch((TOP_ROW+BOT_ROW)/2, i, s[j]); 206 | refresh(); 207 | //wrap_up(); 208 | signal(SIGALRM, SIG_DFL); 209 | sleep(2); 210 | endwin(); 211 | exit(-1); 212 | } 213 | 214 | void change_board(int num) 215 | { 216 | int i; 217 | for (i = the_board.left; i < the_board.left + the_board.length; ++i) 218 | mvaddch(the_board.row, i, ' '); 219 | the_board.left += num; 220 | for (i = the_board.left; i < the_board.left + the_board.length; ++i) 221 | mvaddch(the_board.row, i, '_'); 222 | the_board.how = 0; 223 | //refresh(); 224 | } 225 | 226 | /* [from set_ticker.c] 227 | * set_ticker(number_of_milliseconds) 228 | * arranges for interval timer to issue SIGALRMs at regular intervals 229 | * returns -1 on error, 0 for ok 230 | * arg in milliseconds, converted into whole seconds and mocroseconds 231 | * note: set_ticker(0) turns off ticker 232 | */ 233 | int set_ticker(int n_msecs) 234 | { 235 | struct itimerval new_timeset; 236 | long n_sec, n_usecs; 237 | 238 | n_sec = n_msecs / 1000; // int part 239 | n_usecs = (n_msecs % 1000) * 1000L; // remainder 240 | 241 | new_timeset.it_interval.tv_sec = n_sec; // set reload 242 | new_timeset.it_interval.tv_usec = n_usecs; // new ticker value 243 | 244 | new_timeset.it_value.tv_sec = n_sec; // store this 245 | new_timeset.it_value.tv_usec = n_usecs; 246 | 247 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 248 | } 249 | -------------------------------------------------------------------------------- /07.videogame/bounce_aio: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/bounce_aio -------------------------------------------------------------------------------- /07.videogame/bounce_aio.c: -------------------------------------------------------------------------------- 1 | /* bounce_aio.c 2 | * purpose: animation with user control, using aio_read() etc 3 | * note: set_ticker() sends SIGALRM, handler does animation 4 | * keyboard sends SIGIO, main only calls pause() 5 | * compile: cc bounce_aio.c -lrt -l curses -o bounce_aio 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define MESSAGE "hello" 15 | #define BLANK " " 16 | 17 | int row = 10; 18 | int col = 0; 19 | int dir = 1; 20 | int delay = 200; 21 | int done = 0; 22 | 23 | struct aiocb kbcbuf; // an aio control buf 24 | 25 | main() 26 | { 27 | void on_alarm(int); // handler for alarm 28 | void on_input(int); // handler for keybd 29 | void set_aio_buffer(); 30 | 31 | initscr(); 32 | crmode(); 33 | noecho(); 34 | clear(); 35 | 36 | signal(SIGIO, on_input); // install the handler 37 | set_aio_buffer(); 38 | aio_read(&kbcbuf); 39 | 40 | signal(SIGALRM, on_alarm); 41 | set_ticker(delay); // start ticking 42 | 43 | mvaddstr(row, col, MESSAGE); 44 | 45 | while (1) 46 | { 47 | if (done == 1) break; 48 | pause(); 49 | } 50 | endwin(); 51 | } 52 | 53 | /* 54 | * handler called when aio_read() has stuff to read 55 | * First check for any error codes, and if ok, then get the return code 56 | */ 57 | void on_input(int signum) 58 | { 59 | int c; 60 | char * cp = (char *)kbcbuf.aio_buf; // cast to char * 61 | 62 | // check the error 63 | if (aio_error(&kbcbuf) != 0) 64 | perror("reading failed"); 65 | else 66 | // get number of chars read 67 | if (aio_return(&kbcbuf) == 1) 68 | { 69 | c = *cp; 70 | if (c == 'Q' || c == EOF) 71 | { 72 | done = 1; 73 | } 74 | else if (c == ' ') 75 | { 76 | dir = -dir; 77 | } 78 | } 79 | // place a new request 80 | aio_read(&kbcbuf); 81 | } 82 | 83 | void on_alarm(int signum) 84 | { 85 | signal(SIGALRM, on_alarm); // reset, just in case 86 | mvaddstr(row, col, BLANK); 87 | col += dir; 88 | mvaddstr(row, col, MESSAGE); 89 | refresh(); 90 | 91 | // now handle borders 92 | if (dir == -1 && col <= 0) 93 | dir = 1; 94 | else if (dir == 1 && col + strlen(MESSAGE) >= COLS) 95 | dir = -1; 96 | } 97 | 98 | /* 99 | * set members of struct 100 | * First specify args like those for read(fd, buf, num) and offset 101 | * Then specify what to do(send signal) and what signal(SIGIO) 102 | */ 103 | void set_aio_buffer() 104 | { 105 | static char input[1]; 106 | // describe what to read 107 | kbcbuf.aio_fildes = 0; 108 | kbcbuf.aio_buf = input; 109 | kbcbuf.aio_nbytes = 1; 110 | kbcbuf.aio_offset = 0; 111 | 112 | // describe what to do when read is ready 113 | kbcbuf.aio_sigevent.sigev_notify = SIGEV_SIGNAL; 114 | kbcbuf.aio_sigevent.sigev_signo = SIGIO; 115 | } 116 | 117 | /* [from set_ticker.c] 118 | * set_ticker(number_of_milliseconds) 119 | * arranges for interval timer to issue SIGALRMs at regular intervals 120 | * returns -1 on error, 0 for ok 121 | * arg in milliseconds, converted into whole seconds and mocroseconds 122 | * note: set_ticker(0) turns off ticker 123 | */ 124 | int set_ticker(int n_msecs) 125 | { 126 | struct itimerval new_timeset; 127 | long n_sec, n_usecs; 128 | 129 | n_sec = n_msecs / 1000; // int part 130 | n_usecs = (n_msecs % 1000) * 1000L; // remainder 131 | 132 | new_timeset.it_interval.tv_sec = n_sec; // set reload 133 | new_timeset.it_interval.tv_usec = n_usecs; // new ticker value 134 | 135 | new_timeset.it_value.tv_sec = n_sec; // store this 136 | new_timeset.it_value.tv_usec = n_usecs; 137 | 138 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 139 | } 140 | -------------------------------------------------------------------------------- /07.videogame/bounce_async: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/bounce_async -------------------------------------------------------------------------------- /07.videogame/bounce_async.c: -------------------------------------------------------------------------------- 1 | /* bounce_async.c 2 | * purpose: animation with user control, using O_ASYNC on fd 3 | * note: set_ticker() sends SIGALRM, handler does animation 4 | * keyboard sends SIGIO, main only calls pause() 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MESSAGE "hello" 13 | #define BLANK " " 14 | 15 | int row = 10; 16 | int col = 0; 17 | int dir = 1; 18 | int delay = 200; 19 | int done = 0; 20 | 21 | main() 22 | { 23 | void on_alarm(int); // handler for alarm 24 | void on_input(int); // handler for keybd 25 | void enable_kbd_signals(); 26 | 27 | initscr(); 28 | crmode(); 29 | noecho(); 30 | clear(); 31 | 32 | signal(SIGIO, on_input); // install the handler 33 | enable_kbd_signals(); 34 | signal(SIGALRM, on_alarm); 35 | set_ticker(delay); // start ticking 36 | 37 | move(row, col); 38 | addstr(MESSAGE); 39 | 40 | while (1) 41 | { 42 | if (done == 1) break; 43 | pause(); 44 | } 45 | endwin(); 46 | } 47 | 48 | void on_input(int signum) 49 | { 50 | int c = getch(); 51 | 52 | if (c == 'Q' || c == EOF) 53 | { 54 | done = 1; 55 | } 56 | else if (c == ' ') 57 | { 58 | dir = -dir; 59 | } 60 | } 61 | 62 | void on_alarm(int signum) 63 | { 64 | signal(SIGALRM, on_alarm); // reset, just in case 65 | mvaddstr(row, col, BLANK); 66 | col += dir; 67 | mvaddstr(row, col, MESSAGE); 68 | refresh(); 69 | 70 | // now handle borders 71 | if (dir == -1 && col <= 0) 72 | dir = 1; 73 | else if (dir == 1 && col + strlen(MESSAGE) >= COLS) 74 | dir = -1; 75 | } 76 | 77 | /* 78 | * install a handler, tell kernel who to notify on input, enable 79 | * signals 80 | */ 81 | void enable_kbd_signals() 82 | { 83 | int fd_flags; 84 | 85 | fcntl(0, F_SETOWN, getpid()); 86 | fd_flags = fcntl(0, F_GETFL); 87 | fcntl(0, F_SETFL, (fd_flags|O_ASYNC)); 88 | } 89 | 90 | /* [from set_ticker.c] 91 | * set_ticker(number_of_milliseconds) 92 | * arranges for interval timer to issue SIGALRMs at regular intervals 93 | * returns -1 on error, 0 for ok 94 | * arg in milliseconds, converted into whole seconds and mocroseconds 95 | * note: set_ticker(0) turns off ticker 96 | */ 97 | int set_ticker(int n_msecs) 98 | { 99 | struct itimerval new_timeset; 100 | long n_sec, n_usecs; 101 | 102 | n_sec = n_msecs / 1000; // int part 103 | n_usecs = (n_msecs % 1000) * 1000L; // remainder 104 | 105 | new_timeset.it_interval.tv_sec = n_sec; // set reload 106 | new_timeset.it_interval.tv_usec = n_usecs; // new ticker value 107 | 108 | new_timeset.it_value.tv_sec = n_sec; // store this 109 | new_timeset.it_value.tv_usec = n_usecs; 110 | 111 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 112 | } 113 | -------------------------------------------------------------------------------- /07.videogame/hello1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/hello1 -------------------------------------------------------------------------------- /07.videogame/hello1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello1.c 3 | * purpose: show the minimal calls needed to use curses 4 | * outline: initilize, draw stuff, wait for input, quit 5 | */ 6 | #include 7 | #include 8 | 9 | main() 10 | { 11 | initscr(); // turn on curses, send requests 12 | clear(); // clear screen 13 | move(10, 20); // row 10, col 20 14 | addstr("Hello, World"); // add a string 15 | move(LINES-1, 0); // move to LL 16 | refresh(); // update the screen 17 | getch(); // wait for user input 18 | endwin(); // turn off curses 19 | 20 | } 21 | -------------------------------------------------------------------------------- /07.videogame/hello2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/hello2 -------------------------------------------------------------------------------- /07.videogame/hello2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello2.c 3 | * purpose: show how to use curses functions with a loop 4 | * outline: initilize, draw stuff, wrap up 5 | */ 6 | #include 7 | #include 8 | 9 | main() 10 | { 11 | int i; 12 | 13 | initscr(); // turn on curses, send requests 14 | clear(); // clear screen 15 | for (i = 0; i < LINES; ++i) 16 | { 17 | move(i, i+i); 18 | if (i % 2 == 1) 19 | standout(); 20 | addstr("Hello, World"); 21 | if (i % 2 == 1) 22 | standend(); 23 | } 24 | 25 | refresh(); // update the screen 26 | getch(); // wait for user input 27 | endwin(); // turn off curses 28 | 29 | } 30 | -------------------------------------------------------------------------------- /07.videogame/hello3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/hello3 -------------------------------------------------------------------------------- /07.videogame/hello3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello3.c 3 | * purpose: using refresh and sleep for animated effects 4 | * outline: initilize, draw stuff, wrap up 5 | */ 6 | #include 7 | #include 8 | 9 | main() 10 | { 11 | int i; 12 | 13 | initscr(); // turn on curses, send requests 14 | clear(); // clear screen 15 | for (i = 0; i < LINES; ++i) 16 | { 17 | move(i, i+i); 18 | if (i % 2 == 1) 19 | standout(); 20 | addstr("Hello, World"); 21 | if (i % 2 == 1) 22 | standend(); 23 | sleep(1); 24 | refresh(); 25 | } 26 | 27 | endwin(); // turn off curses 28 | 29 | } 30 | -------------------------------------------------------------------------------- /07.videogame/hello4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/hello4 -------------------------------------------------------------------------------- /07.videogame/hello4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello4.c 3 | * purpose: show how to use erase, time, and draw for animation 4 | */ 5 | #include 6 | #include 7 | 8 | main() 9 | { 10 | int i; 11 | 12 | initscr(); // turn on curses, send requests 13 | clear(); // clear screen 14 | for (i = 0; i < LINES; ++i) 15 | { 16 | move(i, i+i); 17 | if (i % 2 == 1) 18 | standout(); 19 | addstr("Hello, World"); 20 | if (i % 2 == 1) 21 | standend(); 22 | refresh(); 23 | sleep(1); 24 | move(i, i+i); // move back 25 | addstr(" "); // erase line 26 | } 27 | 28 | endwin(); // turn off curses 29 | 30 | } 31 | -------------------------------------------------------------------------------- /07.videogame/hello5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/hello5 -------------------------------------------------------------------------------- /07.videogame/hello5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello5.c 3 | * purpose: bounce a message back and forth across the screen 4 | */ 5 | #include 6 | 7 | #define LEFTEDGE 10 8 | #define RIGHTEDGE 30 9 | #define ROW 10 10 | 11 | main() 12 | { 13 | char * message = "Hello"; 14 | char * blank = " "; 15 | int dir = +1; 16 | int pos = LEFTEDGE; 17 | 18 | initscr(); // turn on curses, send requests 19 | clear(); // clear screen 20 | 21 | 22 | while(1) 23 | { 24 | move(ROW, pos); 25 | addstr( message ); 26 | move(LINES - 1, COLS - 1); // park the cursor 27 | refresh(); 28 | sleep(1); 29 | move(ROW, pos); 30 | addstr( blank ); // erase string 31 | pos += dir; // advance position 32 | if (pos >= RIGHTEDGE) 33 | dir = -1; 34 | if (pos <= LEFTEDGE) 35 | dir = +1; 36 | 37 | } 38 | 39 | endwin(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /07.videogame/sigactdemo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/sigactdemo -------------------------------------------------------------------------------- /07.videogame/sigactdemo.c: -------------------------------------------------------------------------------- 1 | /* sigactdemo.c 2 | * purpose: shows use of sigaction() 3 | * feature: blocks ctrl+\ while handling ctrl+C 4 | * does not reset ctrl+C handler, so two kill 5 | */ 6 | #include 7 | #include 8 | #define INPUTLEN 100 9 | 10 | main() 11 | { 12 | struct sigaction newhandler; // new settings 13 | sigset_t blocked; // set of blocked sigs 14 | void inthandler(); // the handler 15 | char x[INPUTLEN]; 16 | 17 | // load these two members first 18 | newhandler.sa_handler = inthandler; // handler function 19 | newhandler.sa_flags = SA_RESETHAND | SA_RESTART; // options 20 | 21 | // then build the list of blocked signals 22 | sigemptyset(&blocked); // clear all bits 23 | sigaddset(&blocked, SIGQUIT); // add SIGQUIT to list 24 | newhandler.sa_mask = blocked; // store blockmask 25 | 26 | if (sigaction(SIGINT, &newhandler, NULL) == -1) 27 | perror("sigaction"); 28 | else 29 | while(1) 30 | { 31 | fgets(x, INPUTLEN, stdin); 32 | printf("input: %s", x); 33 | } 34 | } 35 | 36 | void inthandler(int s) 37 | { 38 | printf("Called with signal %d\n", s); 39 | sleep(s); 40 | printf("Done handling signal %d\n", s); 41 | } 42 | -------------------------------------------------------------------------------- /07.videogame/sigdemo3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/sigdemo3 -------------------------------------------------------------------------------- /07.videogame/sigdemo3.c: -------------------------------------------------------------------------------- 1 | /* sigdemo3.c 2 | * purpose: show answers to signal questions 3 | * q1: dose the handler stay in effect after a signal arrivers? 4 | * q2: what if a signalX arrivers while handling signalX? 5 | * q3: what if a signalX arrivers while handling signalY? 6 | * q4: what happens to read() when a signal arrives? 7 | */ 8 | #include 9 | #include 10 | 11 | #define INPUTLEN 100 12 | 13 | main(int ac, char * av[]) 14 | { 15 | void inthandler(int); 16 | void quithandle(int); 17 | char input[INPUTLEN]; 18 | int nchars; 19 | 20 | signal(SIGINT, inthandler); // set handler 21 | signal(SIGQUIT, quithandle); // set handler 22 | 23 | do 24 | { 25 | printf("\nType a message\n"); 26 | nchars = read(0, input, (INPUTLEN-1)); 27 | if (nchars == -1) 28 | perror("read returned an error"); 29 | else 30 | { 31 | input[nchars] = '\0'; 32 | printf("You typed: %s", input); 33 | } 34 | } 35 | while (strncmp(input, "quit", 4) != 0); 36 | } 37 | 38 | void inthandler(int s) 39 | { 40 | printf("Received signal %d..waiting\n", s); 41 | sleep(2); 42 | printf("Leaving inthandler \n"); 43 | } 44 | 45 | void quithandle(int s) 46 | { 47 | printf("Received signal %d..waiting\n", s); 48 | sleep(3); 49 | printf("Leaving quithandle \n"); 50 | } 51 | -------------------------------------------------------------------------------- /07.videogame/sleep1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/sleep1 -------------------------------------------------------------------------------- /07.videogame/sleep1.c: -------------------------------------------------------------------------------- 1 | /* sleep1.c 2 | * purpose: show how to sleep works 3 | * usage: sleep1 4 | * outline: sets handler, sets alarm, pauses, then returns 5 | */ 6 | #include 7 | #include 8 | 9 | main() 10 | { 11 | void wakeup(int); 12 | 13 | printf("about to sleep for 4 seconds\n"); 14 | // signal(SIGALRM, wakeup); // catch it 15 | alarm(4); // set clock 16 | pause(); // freeze here 17 | printf("Morning so soon? \n"); // back to work 18 | } 19 | 20 | void wakeup(int signum) 21 | { 22 | printf("Alarm received from kernel\n"); 23 | } 24 | -------------------------------------------------------------------------------- /07.videogame/ticker_demo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/07.videogame/ticker_demo -------------------------------------------------------------------------------- /07.videogame/ticker_demo.c: -------------------------------------------------------------------------------- 1 | /* ticker_demo.c 2 | * demonstrates use of interval timer to generate regular 3 | * signals, which are in turn caught and used to count down 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | void countdown(int); 12 | 13 | signal(SIGALRM, countdown); 14 | if (set_ticker(500) == -1) 15 | perror("set_ticker"); 16 | else 17 | while(1) 18 | pause(); 19 | return 0; 20 | } 21 | 22 | void countdown(int signum) 23 | { 24 | static int num = 10; 25 | printf("%d..", num--); 26 | fflush(stdout); 27 | if (num < 0) 28 | { 29 | printf("DONE! \n"); 30 | exit(0); 31 | } 32 | } 33 | 34 | /* [from set_ticker.c] 35 | * set_ticker(number_of_milliseconds) 36 | * arranges for interval timer to issue SIGALRMs at regular intervals 37 | * returns -1 on error, 0 for ok 38 | * arg in milliseconds, converted into whole seconds and mocroseconds 39 | * note: set_ticker(0) turns off ticker 40 | */ 41 | int set_ticker(int n_msecs) 42 | { 43 | struct itimerval new_timeset; 44 | long n_sec, n_usecs; 45 | 46 | n_sec = n_msecs / 1000; // int part 47 | n_usecs = (n_msecs % 1000) * 1000L; // remainder 48 | 49 | new_timeset.it_interval.tv_sec = n_sec; // set reload 50 | new_timeset.it_interval.tv_usec = n_usecs; // new ticker value 51 | 52 | new_timeset.it_value.tv_sec = n_sec; // store this 53 | new_timeset.it_value.tv_usec = n_usecs; 54 | 55 | return setitimer(ITIMER_REAL, &new_timeset, NULL); 56 | } 57 | -------------------------------------------------------------------------------- /08.sh/psh1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/08.sh/psh1 -------------------------------------------------------------------------------- /08.sh/psh1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * prompting shell version 1 3 | * Prompts for the command and its arguments 4 | * Builds the argument vector for the call to execvp 5 | * Uses execvp(), and never returns 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define MAXARGS 20 // cmdline args 13 | #define ARGLEN 100 // token length 14 | 15 | int main() 16 | { 17 | char *arglist[MAXARGS + 1]; // an array of ptrs 18 | int numargs; // index into array 19 | char argbuf[ARGLEN]; // read stuff here 20 | char * makestring(); // malloc etc 21 | 22 | numargs = 0; 23 | while (numargs < MAXARGS) 24 | { 25 | printf("Arg[%d]?", numargs); 26 | if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n') 27 | arglist[numargs ++] = makestring(argbuf); 28 | else 29 | { 30 | if (numargs > 0) 31 | { 32 | arglist[numargs] = NULL; // close list 33 | execute(arglist); 34 | numargs = 0; 35 | } 36 | } 37 | } 38 | return 0; 39 | } 40 | 41 | int execute(char *arglist[]) 42 | /* 43 | * use execvp to do it 44 | */ 45 | { 46 | execvp(arglist[0], arglist); 47 | perror("execvp failed"); 48 | exit(1); 49 | } 50 | 51 | char * makestring(char *buf) 52 | /* 53 | * trim off newline and create storage for the string 54 | */ 55 | { 56 | char *cp, *malloc(); 57 | 58 | buf[strlen(buf) - 1] = '\0'; // trim newline 59 | cp = malloc(strlen(buf) + 1); // get memory 60 | if (cp == NULL) 61 | { 62 | fprintf(stderr, "no memory\n"); 63 | exit(1); 64 | } 65 | strcpy(cp, buf); 66 | return cp; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /08.sh/psh2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/08.sh/psh2 -------------------------------------------------------------------------------- /08.sh/psh2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * prompting shell version 2 3 | * Solves the 'one-shot' problem of version 1 4 | * Uses execvp(), but fork()s first so that the 5 | * shell waits around to perform another command 6 | * New problem: shell catches signals. Run vi, press ^c 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #define MAXARGS 20 // cmdline args 13 | #define ARGLEN 100 // token length 14 | 15 | int main() 16 | { 17 | char *arglist[MAXARGS + 1]; // an array of ptrs 18 | int numargs; // index into array 19 | char argbuf[ARGLEN]; // read stuff here 20 | char * makestring(); // malloc etc 21 | 22 | numargs = 0; 23 | while (numargs < MAXARGS) 24 | { 25 | printf("Arg[%d]?", numargs); 26 | if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n') 27 | arglist[numargs ++] = makestring(argbuf); 28 | else 29 | { 30 | if (numargs > 0) 31 | { 32 | arglist[numargs] = NULL; // close list 33 | execute(arglist); 34 | numargs = 0; 35 | } 36 | } 37 | } 38 | return 0; 39 | } 40 | 41 | int execute(char *arglist[]) 42 | /* 43 | * use fork and execvp and wait to do it 44 | */ 45 | { 46 | int pid, exitstatus; 47 | 48 | pid = fork(); 49 | switch(pid) 50 | { 51 | case -1: 52 | perror("fork failed"); 53 | exit(1); 54 | case 0: 55 | execvp(arglist[0], arglist); 56 | perror("execvp failed"); 57 | exit(1); 58 | default: 59 | while (wait(&exitstatus) != pid) 60 | ; 61 | printf("child exited with status %d, %d\n", 62 | exitstatus>>8, exitstatus & 0377); 63 | } 64 | } 65 | 66 | char * makestring(char *buf) 67 | /* 68 | * trim off newline and create storage for the string 69 | */ 70 | { 71 | char *cp, *malloc(); 72 | 73 | buf[strlen(buf) - 1] = '\0'; // trim newline 74 | cp = malloc(strlen(buf) + 1); // get memory 75 | if (cp == NULL) 76 | { 77 | fprintf(stderr, "no memory\n"); 78 | exit(1); 79 | } 80 | strcpy(cp, buf); 81 | return cp; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /09.shell/smsh1/execute.c: -------------------------------------------------------------------------------- 1 | // execute.c - code used by small shell to execute commands 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int execute(char *argv[]) 10 | /* 11 | * purpose: run a program passing it arguments 12 | * returns: status returned via wait, or -1 on error 13 | * errors: -1 on fork() or wait() errors 14 | */ 15 | { 16 | int pid; 17 | int child_info = -1; 18 | 19 | if (argv[0] == NULL) 20 | return 0; 21 | 22 | if ((pid = fork()) == -1) 23 | perror("fork"); 24 | else if (pid == 0) 25 | { 26 | signal(SIGINT, SIG_DFL); 27 | signal(SIGQUIT, SIG_DFL); 28 | execvp(argv[0], argv); 29 | perror("cannot execute command"); 30 | exit(1); 31 | } 32 | else 33 | { 34 | if (wait(&child_info) == -1) 35 | perror("wait"); 36 | } 37 | return child_info; 38 | } 39 | -------------------------------------------------------------------------------- /09.shell/smsh1/smsh.h: -------------------------------------------------------------------------------- 1 | #define YES 1 2 | #define NO 0 3 | 4 | char *next_cmd(); 5 | char ** splitline(char *); 6 | void freelist(char **); 7 | void * emalloc(size_t); 8 | void * erealloc(void *, size_t); 9 | int execute(char **); 10 | void fatal(char *, char *, int); 11 | -------------------------------------------------------------------------------- /09.shell/smsh1/smsh1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/09.shell/smsh1/smsh1 -------------------------------------------------------------------------------- /09.shell/smsh1/smsh1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * smsh1.c small-shell version 1 3 | * 4 | * first really useful version after prompting shell 5 | * this one parses the command line into strings 6 | * uses fork, exec, wait, and ignores signals 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "smsh.h" 13 | 14 | #define DFL_PROMPT ">" 15 | 16 | int main() 17 | { 18 | char * cmdline, *prompt, ** arglist; 19 | int result; 20 | void setup(); 21 | 22 | prompt = DFL_PROMPT; 23 | setup(); 24 | 25 | while ((cmdline = next_cmd(prompt, stdin)) != NULL) 26 | { 27 | if ((arglist = splitline(cmdline)) != NULL) 28 | { 29 | result = execute(arglist); 30 | freelist(arglist); 31 | } 32 | free(cmdline); 33 | } 34 | return 0; 35 | } 36 | 37 | void setup() 38 | /* 39 | * purpose: initialize shell 40 | * returns: nothing. calls fatal() if trouble 41 | */ 42 | { 43 | signal(SIGINT, SIG_IGN); 44 | signal(SIGQUIT, SIG_IGN); 45 | } 46 | 47 | void fatal(char *s1, char *s2, int n) 48 | { 49 | fprintf(stderr, "Error: %s, %s\n", s1, s2); 50 | exit(n); 51 | } 52 | -------------------------------------------------------------------------------- /09.shell/smsh1/splitline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * splitline.c - command reading and parsing functions for smsh 3 | * 4 | * char * next_cmd(char *prompt, FILE *fp) - get next command 5 | * char ** splitline(char *str) - parse a string 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "smsh.h" 11 | 12 | char * next_cmd(char * prompt, FILE * fp) 13 | /* 14 | * purpose: read next command line from fp 15 | * returns: dynamically allocated string holding command line 16 | * errors: NULL at EOF (not really an error) 17 | * calls fatal from emalloc() 18 | * notes: allocates space in BUFSIZ chunks 19 | */ 20 | { 21 | char *buf; // the buffer 22 | int bufspace = 0; // total size 23 | int pos = 0; // current position 24 | int c; // input char 25 | 26 | printf("%s", prompt); 27 | while ((c = getc(fp)) != EOF) 28 | { 29 | if (pos + 1 >= bufspace) 30 | { 31 | if (bufspace == 0) 32 | buf = emalloc(BUFSIZ); 33 | else 34 | buf = erealloc(buf, bufspace+BUFSIZ); 35 | bufspace += BUFSIZ; 36 | } 37 | 38 | if (c == '\n') 39 | break; 40 | 41 | buf[pos++] = c; 42 | } 43 | if (c == EOF && pos == 0) 44 | return NULL; 45 | buf[pos] = '\0'; 46 | return buf; 47 | } 48 | 49 | /* 50 | * splitline (parse a line into an array of strings) 51 | */ 52 | char ** splitline(char * line) 53 | /* 54 | * purpose: split a line into array of white - space separated tokens 55 | * returns: a NULL-terminated array of pointers to copies of the 56 | * tokens or NULL if line if no tokens on the line 57 | * action: travers the array, locate strings, make copies 58 | * note: strtok() could work, but we may want to add quotes later 59 | */ 60 | { 61 | char * newstr(); 62 | char **args; 63 | int spots = 0; // spots in table 64 | int bufspace = 0; // bytes in table 65 | int argnum = 0; // slots used 66 | char *cp = line; // pos in string 67 | char *start; 68 | int len; 69 | 70 | if (line == NULL) 71 | return NULL; 72 | 73 | args = emalloc(BUFSIZ); 74 | bufspace = BUFSIZ; 75 | spots = BUFSIZ / sizeof(char *); 76 | 77 | while (*cp != '\0') 78 | { 79 | while (isspace(*cp)) // skip leading spaces 80 | cp++; 81 | if (*cp == "\0") 82 | break; 83 | 84 | // make sure the array has room(+1 for NULL) 85 | if (argnum + 1 >= spots) 86 | { 87 | args = erealloc(args, bufspace+BUFSIZ); 88 | bufspace += BUFSIZ; 89 | spots += (BUFSIZ/sizeof(char *)); 90 | } 91 | // mark start, then find end of word 92 | start = cp; 93 | len = 1; 94 | while (*++cp != '\0' && !(isspace(*cp))) 95 | { 96 | len++; 97 | } 98 | args[argnum++] = newstr(start, len); 99 | } 100 | 101 | args[argnum] = NULL; 102 | return args; 103 | } 104 | 105 | /* 106 | * purpose: constructor for strings 107 | * returns: a string, never NULL 108 | */ 109 | char * newstr(char *s, int l) 110 | { 111 | char * rv = emalloc(l + 1); 112 | 113 | rv[l] = '\0'; 114 | strncpy(rv, s, l); 115 | return rv; 116 | } 117 | 118 | void freelist(char ** list) 119 | /* 120 | * purpose: free the list returned by splitline 121 | * returns: nothing 122 | * action: free all strings in list and then free the list 123 | */ 124 | { 125 | char ** cp = list; 126 | while (*cp) 127 | free(*cp++); 128 | free(list); 129 | } 130 | 131 | void * emalloc(size_t n) 132 | { 133 | void * rv; 134 | if ((rv = malloc(n)) == NULL) 135 | fatal("out of memory", "", 1); 136 | return rv; 137 | } 138 | 139 | void * erealloc(void *p, size_t n) 140 | { 141 | void *rv; 142 | if ((rv = realloc(p, n)) == NULL) 143 | fatal("realloc() failed", "", 1); 144 | return rv; 145 | } 146 | -------------------------------------------------------------------------------- /09.shell/smsh2/controlflow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * controlflow.c 3 | * 4 | * "if" processing is done with two state variables 5 | * if_state and if_result 6 | */ 7 | #include 8 | #include "smsh.h" 9 | 10 | enum states {NEUTRAL, WANT_THEN, THEN_BLOCK}; 11 | enum results {SUCESS, FAIL}; 12 | 13 | static int if_state = NEUTRAL; 14 | static int if_result = SUCESS; 15 | static int last_stat = 0; 16 | 17 | int syn_err(char *); 18 | 19 | int ok_to_execute() 20 | /* 21 | * purpose: determine the shell should execute a command 22 | * returns: 1 for yes, 0 for no 23 | * details: if in THEN_BLOCK and if_result was SUCESS then yes 24 | * if in THEN_BLOCK and if_result was FAIL then no 25 | * if in WANT_THEN then syntax error(sh is different) 26 | */ 27 | { 28 | int rv = 1; 29 | 30 | if (if_state == WANT_THEN) 31 | { 32 | syn_err("then expected"); 33 | rv = 0; 34 | } 35 | else if (if_state == THEN_BLOCK && if_result == SUCESS) 36 | rv = 1; 37 | else if (if_state == THEN_BLOCK && if_result == FAIL) 38 | rv = 0; 39 | return rv; 40 | } 41 | 42 | int is_control_command(char *s) 43 | /* 44 | * purpose: boolean to report if the command is a shell control command 45 | * returns: 0 or 1 46 | */ 47 | { 48 | return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 || 49 | strcmp(s, "fi") == 0); 50 | } 51 | 52 | int do_control_command(char **args) 53 | /* 54 | * purpose: Porcess "if", "then", "fi" - change state or detect error 55 | * returns: 0 if ok, -1 for syntax error 56 | */ 57 | { 58 | char * cmd = args[0]; 59 | int rv = -1; 60 | 61 | if (strcmp(cmd, "if") == 0) 62 | { 63 | if (if_state != NEUTRAL) 64 | rv = syn_err("if unexpected"); 65 | else 66 | { 67 | last_stat = process(args+1); 68 | if_result = (last_stat == 0 ? SUCESS: FAIL); 69 | if_state = WANT_THEN; 70 | rv = 0; 71 | } 72 | } 73 | else if (strcmp(cmd, "then") == 0) 74 | { 75 | if (if_state != WANT_THEN) 76 | rv = syn_err("then unexpected"); 77 | else 78 | { 79 | if_state = THEN_BLOCK; 80 | rv = 0; 81 | } 82 | } 83 | else if (strcmp(cmd, "fi") == 0) 84 | { 85 | if (if_state != THEN_BLOCK) 86 | rv = syn_err("fi unexpected"); 87 | else 88 | { 89 | if_state = NEUTRAL; 90 | rv = 0; 91 | } 92 | } 93 | else 94 | fatal("internal error processing:", cmd, 2); 95 | 96 | return rv; 97 | } 98 | 99 | int syn_err(char * msg) 100 | /* 101 | * purpose: handles syntax errors in control structures 102 | * details: resets state to NEUTRAL 103 | * returns: -1 in interactive mode. Should call fatal in scripts 104 | */ 105 | { 106 | if_state = NEUTRAL; 107 | fprintf(stderr, "syntax error: %s\n", msg); 108 | return -1; 109 | } 110 | -------------------------------------------------------------------------------- /09.shell/smsh2/execute.c: -------------------------------------------------------------------------------- 1 | // execute.c - code used by small shell to execute commands 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int execute(char *argv[]) 10 | /* 11 | * purpose: run a program passing it arguments 12 | * returns: status returned via wait, or -1 on error 13 | * errors: -1 on fork() or wait() errors 14 | */ 15 | { 16 | int pid; 17 | int child_info = -1; 18 | 19 | if (argv[0] == NULL) 20 | return 0; 21 | 22 | if ((pid = fork()) == -1) 23 | perror("fork"); 24 | else if (pid == 0) 25 | { 26 | signal(SIGINT, SIG_DFL); 27 | signal(SIGQUIT, SIG_DFL); 28 | execvp(argv[0], argv); 29 | perror("cannot execute command"); 30 | exit(1); 31 | } 32 | else 33 | { 34 | if (wait(&child_info) == -1) 35 | perror("wait"); 36 | } 37 | return child_info; 38 | } 39 | -------------------------------------------------------------------------------- /09.shell/smsh2/process.c: -------------------------------------------------------------------------------- 1 | /* 2 | * process.c 3 | * command processing layer 4 | * 5 | * The process(char ** arglist) function is called by the main loop 6 | * It sits in front of the execute() function. This layer handles 7 | * two main classes of processing. 8 | * a) built-in functions(e.g. exit(), set, =, read,..) 9 | * b) control structures(e.g. if, while, for) 10 | */ 11 | 12 | #include 13 | #include "smsh.h" 14 | 15 | int is_control_command(char *); 16 | int do_control_command(char **); 17 | int ok_to_execute(); 18 | 19 | int process(char **args) 20 | /* 21 | * purpose: process user command 22 | * returns: result of proceessing command 23 | * details: if a built-in then call arrprporiate function, if not 24 | * execute() 25 | * errors: arise from subroutines, handled there 26 | */ 27 | { 28 | int rv = 0; 29 | 30 | if (args[0] == NULL) 31 | rv = 0; 32 | else if (is_control_command(args[0])) 33 | rv = do_control_command(args); 34 | else if (ok_to_execute()) 35 | rv = execute(args); 36 | return rv; 37 | } 38 | -------------------------------------------------------------------------------- /09.shell/smsh2/smsh.h: -------------------------------------------------------------------------------- 1 | #define YES 1 2 | #define NO 0 3 | 4 | char *next_cmd(); 5 | char ** splitline(char *); 6 | void freelist(char **); 7 | void * emalloc(size_t); 8 | void * erealloc(void *, size_t); 9 | int execute(char **); 10 | void fatal(char *, char *, int); 11 | -------------------------------------------------------------------------------- /09.shell/smsh2/smsh2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/09.shell/smsh2/smsh2 -------------------------------------------------------------------------------- /09.shell/smsh2/smsh2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * smsh2.c small-shell version 2 3 | * 4 | * small shell that supports command line parsing 5 | * and if..then..else.fi logic(by calling process()) 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "smsh.h" 12 | 13 | #define DFL_PROMPT ">" 14 | 15 | int main() 16 | { 17 | char * cmdline, *prompt, ** arglist; 18 | int result; 19 | void setup(); 20 | 21 | prompt = DFL_PROMPT; 22 | setup(); 23 | 24 | while ((cmdline = next_cmd(prompt, stdin)) != NULL) 25 | { 26 | if ((arglist = splitline(cmdline)) != NULL) 27 | { 28 | result = process(arglist); 29 | freelist(arglist); 30 | } 31 | free(cmdline); 32 | } 33 | return 0; 34 | } 35 | 36 | void setup() 37 | /* 38 | * purpose: initialize shell 39 | * returns: nothing. calls fatal() if trouble 40 | */ 41 | { 42 | signal(SIGINT, SIG_IGN); 43 | signal(SIGQUIT, SIG_IGN); 44 | } 45 | 46 | void fatal(char *s1, char *s2, int n) 47 | { 48 | fprintf(stderr, "Error: %s, %s\n", s1, s2); 49 | exit(n); 50 | } 51 | -------------------------------------------------------------------------------- /09.shell/smsh2/splitline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * splitline.c - command reading and parsing functions for smsh 3 | * 4 | * char * next_cmd(char *prompt, FILE *fp) - get next command 5 | * char ** splitline(char *str) - parse a string 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "smsh.h" 11 | 12 | char * next_cmd(char * prompt, FILE * fp) 13 | /* 14 | * purpose: read next command line from fp 15 | * returns: dynamically allocated string holding command line 16 | * errors: NULL at EOF (not really an error) 17 | * calls fatal from emalloc() 18 | * notes: allocates space in BUFSIZ chunks 19 | */ 20 | { 21 | char *buf; // the buffer 22 | int bufspace = 0; // total size 23 | int pos = 0; // current position 24 | int c; // input char 25 | 26 | printf("%s", prompt); 27 | while ((c = getc(fp)) != EOF) 28 | { 29 | if (pos + 1 >= bufspace) 30 | { 31 | if (bufspace == 0) 32 | buf = emalloc(BUFSIZ); 33 | else 34 | buf = erealloc(buf, bufspace+BUFSIZ); 35 | bufspace += BUFSIZ; 36 | } 37 | 38 | if (c == '\n') 39 | break; 40 | 41 | buf[pos++] = c; 42 | } 43 | if (c == EOF && pos == 0) 44 | return NULL; 45 | buf[pos] = '\0'; 46 | return buf; 47 | } 48 | 49 | /* 50 | * splitline (parse a line into an array of strings) 51 | */ 52 | char ** splitline(char * line) 53 | /* 54 | * purpose: split a line into array of white - space separated tokens 55 | * returns: a NULL-terminated array of pointers to copies of the 56 | * tokens or NULL if line if no tokens on the line 57 | * action: travers the array, locate strings, make copies 58 | * note: strtok() could work, but we may want to add quotes later 59 | */ 60 | { 61 | char * newstr(); 62 | char **args; 63 | int spots = 0; // spots in table 64 | int bufspace = 0; // bytes in table 65 | int argnum = 0; // slots used 66 | char *cp = line; // pos in string 67 | char *start; 68 | int len; 69 | 70 | if (line == NULL) 71 | return NULL; 72 | 73 | args = emalloc(BUFSIZ); 74 | bufspace = BUFSIZ; 75 | spots = BUFSIZ / sizeof(char *); 76 | 77 | while (*cp != '\0') 78 | { 79 | while (isspace(*cp)) // skip leading spaces 80 | cp++; 81 | if (*cp == "\0") 82 | break; 83 | 84 | // make sure the array has room(+1 for NULL) 85 | if (argnum + 1 >= spots) 86 | { 87 | args = erealloc(args, bufspace+BUFSIZ); 88 | bufspace += BUFSIZ; 89 | spots += (BUFSIZ/sizeof(char *)); 90 | } 91 | // mark start, then find end of word 92 | start = cp; 93 | len = 1; 94 | while (*++cp != '\0' && !(isspace(*cp))) 95 | { 96 | len++; 97 | } 98 | args[argnum++] = newstr(start, len); 99 | } 100 | 101 | args[argnum] = NULL; 102 | return args; 103 | } 104 | 105 | /* 106 | * purpose: constructor for strings 107 | * returns: a string, never NULL 108 | */ 109 | char * newstr(char *s, int l) 110 | { 111 | char * rv = emalloc(l + 1); 112 | 113 | rv[l] = '\0'; 114 | strncpy(rv, s, l); 115 | return rv; 116 | } 117 | 118 | void freelist(char ** list) 119 | /* 120 | * purpose: free the list returned by splitline 121 | * returns: nothing 122 | * action: free all strings in list and then free the list 123 | */ 124 | { 125 | char ** cp = list; 126 | while (*cp) 127 | free(*cp++); 128 | free(list); 129 | } 130 | 131 | void * emalloc(size_t n) 132 | { 133 | void * rv; 134 | if ((rv = malloc(n)) == NULL) 135 | fatal("out of memory", "", 1); 136 | return rv; 137 | } 138 | 139 | void * erealloc(void *p, size_t n) 140 | { 141 | void *rv; 142 | if ((rv = realloc(p, n)) == NULL) 143 | fatal("realloc() failed", "", 1); 144 | return rv; 145 | } 146 | -------------------------------------------------------------------------------- /09.shell/smsh3/builtin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * builtin.c 3 | * contains the switch and the functions for builtin commands 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include "smsh.h" 9 | #include "varlib.h" 10 | 11 | int assign(char *); 12 | int okname(char *); 13 | 14 | int builtin_command(char **args, int *resultp) 15 | /* 16 | * purpose: run a builtin command 17 | * returns: 1 if args[0] is builtin, 0 if not 18 | * details: test args[0] against all known built-ins. Call functions 19 | */ 20 | { 21 | int rv = 0; 22 | 23 | if (strcmp(args[0], "set") == 0) 24 | { 25 | VLlist(); 26 | *resultp = 0; 27 | rv = 1; 28 | } 29 | else if (strchr(args[0], '=') != NULL) 30 | { 31 | *resultp = assign(args[0]); 32 | if (*resultp != -1) 33 | rv = 1; 34 | } 35 | else if (strcmp(args[0], "export") == 0) 36 | { 37 | if (args[1] != NULL && okname(args[1])) 38 | *resultp = VLexport(args[1]); 39 | else 40 | *resultp = 1; 41 | rv = 1; 42 | } 43 | return rv; 44 | } 45 | 46 | int assign(char *str) 47 | /* 48 | * purpose: execute name = val AND ensure that name is legal 49 | * returns: -1 for illegal lval, or result of VLstore 50 | * warning: modifies the string, but restores it to normal 51 | */ 52 | { 53 | char *cp; 54 | int rv; 55 | 56 | cp = strchr(str, '='); 57 | *cp = '\0'; 58 | rv = (okname(str) ? VLstore(str, cp+1) : -1); 59 | *cp = '='; 60 | return rv; 61 | } 62 | 63 | int okname(char *str) 64 | /* 65 | * purpose: determines if a string is a legal variable name 66 | * returns: 0 for no, 1 for yes 67 | */ 68 | { 69 | char *cp; 70 | 71 | for (cp = str; *cp; cp++) 72 | { 73 | if ((isdigit(*cp) && cp == str) || !(isalnum(*cp)) || *cp == '_') 74 | return 0; 75 | } 76 | 77 | return (cp != str); 78 | } 79 | -------------------------------------------------------------------------------- /09.shell/smsh3/controlflow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * controlflow.c 3 | * 4 | * "if" processing is done with two state variables 5 | * if_state and if_result 6 | */ 7 | #include 8 | #include "smsh.h" 9 | 10 | enum states {NEUTRAL, WANT_THEN, THEN_BLOCK}; 11 | enum results {SUCESS, FAIL}; 12 | 13 | static int if_state = NEUTRAL; 14 | static int if_result = SUCESS; 15 | static int last_stat = 0; 16 | 17 | int syn_err(char *); 18 | 19 | int ok_to_execute() 20 | /* 21 | * purpose: determine the shell should execute a command 22 | * returns: 1 for yes, 0 for no 23 | * details: if in THEN_BLOCK and if_result was SUCESS then yes 24 | * if in THEN_BLOCK and if_result was FAIL then no 25 | * if in WANT_THEN then syntax error(sh is different) 26 | */ 27 | { 28 | int rv = 1; 29 | 30 | if (if_state == WANT_THEN) 31 | { 32 | syn_err("then expected"); 33 | rv = 0; 34 | } 35 | else if (if_state == THEN_BLOCK && if_result == SUCESS) 36 | rv = 1; 37 | else if (if_state == THEN_BLOCK && if_result == FAIL) 38 | rv = 0; 39 | return rv; 40 | } 41 | 42 | int is_control_command(char *s) 43 | /* 44 | * purpose: boolean to report if the command is a shell control command 45 | * returns: 0 or 1 46 | */ 47 | { 48 | return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 || 49 | strcmp(s, "fi") == 0); 50 | } 51 | 52 | int do_control_command(char **args) 53 | /* 54 | * purpose: Porcess "if", "then", "fi" - change state or detect error 55 | * returns: 0 if ok, -1 for syntax error 56 | */ 57 | { 58 | char * cmd = args[0]; 59 | int rv = -1; 60 | 61 | if (strcmp(cmd, "if") == 0) 62 | { 63 | if (if_state != NEUTRAL) 64 | rv = syn_err("if unexpected"); 65 | else 66 | { 67 | last_stat = process(args+1); 68 | if_result = (last_stat == 0 ? SUCESS: FAIL); 69 | if_state = WANT_THEN; 70 | rv = 0; 71 | } 72 | } 73 | else if (strcmp(cmd, "then") == 0) 74 | { 75 | if (if_state != WANT_THEN) 76 | rv = syn_err("then unexpected"); 77 | else 78 | { 79 | if_state = THEN_BLOCK; 80 | rv = 0; 81 | } 82 | } 83 | else if (strcmp(cmd, "fi") == 0) 84 | { 85 | if (if_state != THEN_BLOCK) 86 | rv = syn_err("fi unexpected"); 87 | else 88 | { 89 | if_state = NEUTRAL; 90 | rv = 0; 91 | } 92 | } 93 | else 94 | fatal("internal error processing:", cmd, 2); 95 | 96 | return rv; 97 | } 98 | 99 | int syn_err(char * msg) 100 | /* 101 | * purpose: handles syntax errors in control structures 102 | * details: resets state to NEUTRAL 103 | * returns: -1 in interactive mode. Should call fatal in scripts 104 | */ 105 | { 106 | if_state = NEUTRAL; 107 | fprintf(stderr, "syntax error: %s\n", msg); 108 | return -1; 109 | } 110 | -------------------------------------------------------------------------------- /09.shell/smsh3/execute.c: -------------------------------------------------------------------------------- 1 | // execute.c - code used by small shell to execute commands 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int execute(char *argv[]) 10 | /* 11 | * purpose: run a program passing it arguments 12 | * returns: status returned via wait, or -1 on error 13 | * errors: -1 on fork() or wait() errors 14 | */ 15 | { 16 | int pid; 17 | int child_info = -1; 18 | 19 | if (argv[0] == NULL) 20 | return 0; 21 | 22 | if ((pid = fork()) == -1) 23 | perror("fork"); 24 | else if (pid == 0) 25 | { 26 | signal(SIGINT, SIG_DFL); 27 | signal(SIGQUIT, SIG_DFL); 28 | execvp(argv[0], argv); 29 | perror("cannot execute command"); 30 | exit(1); 31 | } 32 | else 33 | { 34 | if (wait(&child_info) == -1) 35 | perror("wait"); 36 | } 37 | return child_info; 38 | } 39 | -------------------------------------------------------------------------------- /09.shell/smsh3/process.c: -------------------------------------------------------------------------------- 1 | /* 2 | * process.c 3 | * command processing layer 4 | * 5 | * The process(char ** arglist) function is called by the main loop 6 | * It sits in front of the execute() function. This layer handles 7 | * two main classes of processing. 8 | * a) built-in functions(e.g. exit(), set, =, read,..) 9 | * b) control structures(e.g. if, while, for) 10 | */ 11 | 12 | #include 13 | #include "smsh.h" 14 | 15 | int is_control_command(char *); 16 | int do_control_command(char **); 17 | int ok_to_execute(); 18 | 19 | int process(char **args) 20 | /* 21 | * purpose: process user command 22 | * returns: result of proceessing command 23 | * details: if a built-in then call arrprporiate function, if not 24 | * execute() 25 | * errors: arise from subroutines, handled there 26 | */ 27 | { 28 | int rv = 0; 29 | 30 | if (args[0] == NULL) 31 | rv = 0; 32 | else if (is_control_command(args[0])) 33 | rv = do_control_command(args); 34 | else if (ok_to_execute()) 35 | { 36 | if (! builtin_command(args, &rv)) 37 | rv = execute(args); 38 | } 39 | return rv; 40 | } 41 | -------------------------------------------------------------------------------- /09.shell/smsh3/smsh.h: -------------------------------------------------------------------------------- 1 | #define YES 1 2 | #define NO 0 3 | 4 | char *next_cmd(); 5 | char ** splitline(char *); 6 | void freelist(char **); 7 | void * emalloc(size_t); 8 | void * erealloc(void *, size_t); 9 | int execute(char **); 10 | void fatal(char *, char *, int); 11 | -------------------------------------------------------------------------------- /09.shell/smsh3/smsh3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/09.shell/smsh3/smsh3 -------------------------------------------------------------------------------- /09.shell/smsh3/smsh3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * smsh3.c small-shell version 3 3 | * 4 | * small shell that supports command line parsing 5 | * and if..then..else.fi logic(by calling process()) 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "smsh.h" 12 | 13 | #define DFL_PROMPT ">" 14 | 15 | int main() 16 | { 17 | char * cmdline, *prompt, ** arglist; 18 | int result; 19 | void setup(); 20 | 21 | prompt = DFL_PROMPT; 22 | setup(); 23 | 24 | while ((cmdline = next_cmd(prompt, stdin)) != NULL) 25 | { 26 | if ((arglist = splitline(cmdline)) != NULL) 27 | { 28 | result = process(arglist); 29 | freelist(arglist); 30 | } 31 | free(cmdline); 32 | } 33 | return 0; 34 | } 35 | 36 | void setup() 37 | /* 38 | * purpose: initialize shell 39 | * returns: nothing. calls fatal() if trouble 40 | */ 41 | { 42 | signal(SIGINT, SIG_IGN); 43 | signal(SIGQUIT, SIG_IGN); 44 | } 45 | 46 | void fatal(char *s1, char *s2, int n) 47 | { 48 | fprintf(stderr, "Error: %s, %s\n", s1, s2); 49 | exit(n); 50 | } 51 | -------------------------------------------------------------------------------- /09.shell/smsh3/splitline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * splitline.c - command reading and parsing functions for smsh 3 | * 4 | * char * next_cmd(char *prompt, FILE *fp) - get next command 5 | * char ** splitline(char *str) - parse a string 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "smsh.h" 11 | 12 | char * next_cmd(char * prompt, FILE * fp) 13 | /* 14 | * purpose: read next command line from fp 15 | * returns: dynamically allocated string holding command line 16 | * errors: NULL at EOF (not really an error) 17 | * calls fatal from emalloc() 18 | * notes: allocates space in BUFSIZ chunks 19 | */ 20 | { 21 | char *buf; // the buffer 22 | int bufspace = 0; // total size 23 | int pos = 0; // current position 24 | int c; // input char 25 | 26 | printf("%s", prompt); 27 | while ((c = getc(fp)) != EOF) 28 | { 29 | if (pos + 1 >= bufspace) 30 | { 31 | if (bufspace == 0) 32 | buf = emalloc(BUFSIZ); 33 | else 34 | buf = erealloc(buf, bufspace+BUFSIZ); 35 | bufspace += BUFSIZ; 36 | } 37 | 38 | if (c == '\n') 39 | break; 40 | 41 | buf[pos++] = c; 42 | } 43 | if (c == EOF && pos == 0) 44 | return NULL; 45 | buf[pos] = '\0'; 46 | return buf; 47 | } 48 | 49 | /* 50 | * splitline (parse a line into an array of strings) 51 | */ 52 | char ** splitline(char * line) 53 | /* 54 | * purpose: split a line into array of white - space separated tokens 55 | * returns: a NULL-terminated array of pointers to copies of the 56 | * tokens or NULL if line if no tokens on the line 57 | * action: travers the array, locate strings, make copies 58 | * note: strtok() could work, but we may want to add quotes later 59 | */ 60 | { 61 | char * newstr(); 62 | char **args; 63 | int spots = 0; // spots in table 64 | int bufspace = 0; // bytes in table 65 | int argnum = 0; // slots used 66 | char *cp = line; // pos in string 67 | char *start; 68 | int len; 69 | 70 | if (line == NULL) 71 | return NULL; 72 | 73 | args = emalloc(BUFSIZ); 74 | bufspace = BUFSIZ; 75 | spots = BUFSIZ / sizeof(char *); 76 | 77 | while (*cp != '\0') 78 | { 79 | while (isspace(*cp)) // skip leading spaces 80 | cp++; 81 | if (*cp == "\0") 82 | break; 83 | 84 | // make sure the array has room(+1 for NULL) 85 | if (argnum + 1 >= spots) 86 | { 87 | args = erealloc(args, bufspace+BUFSIZ); 88 | bufspace += BUFSIZ; 89 | spots += (BUFSIZ/sizeof(char *)); 90 | } 91 | // mark start, then find end of word 92 | start = cp; 93 | len = 1; 94 | while (*++cp != '\0' && !(isspace(*cp))) 95 | { 96 | len++; 97 | } 98 | args[argnum++] = newstr(start, len); 99 | } 100 | 101 | args[argnum] = NULL; 102 | return args; 103 | } 104 | 105 | /* 106 | * purpose: constructor for strings 107 | * returns: a string, never NULL 108 | */ 109 | char * newstr(char *s, int l) 110 | { 111 | char * rv = emalloc(l + 1); 112 | 113 | rv[l] = '\0'; 114 | strncpy(rv, s, l); 115 | return rv; 116 | } 117 | 118 | void freelist(char ** list) 119 | /* 120 | * purpose: free the list returned by splitline 121 | * returns: nothing 122 | * action: free all strings in list and then free the list 123 | */ 124 | { 125 | char ** cp = list; 126 | while (*cp) 127 | free(*cp++); 128 | free(list); 129 | } 130 | 131 | void * emalloc(size_t n) 132 | { 133 | void * rv; 134 | if ((rv = malloc(n)) == NULL) 135 | fatal("out of memory", "", 1); 136 | return rv; 137 | } 138 | 139 | void * erealloc(void *p, size_t n) 140 | { 141 | void *rv; 142 | if ((rv = realloc(p, n)) == NULL) 143 | fatal("realloc() failed", "", 1); 144 | return rv; 145 | } 146 | -------------------------------------------------------------------------------- /09.shell/smsh3/varlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * varlib.c 3 | * 4 | * a simple storage system to store name=value pairs 5 | * with facility to mark items as part of the environment 6 | * 7 | * interface: 8 | * VLstore(name, value) return 1 for ok, 0 for no 9 | * VLlookup(name) return string or NULL if not there 10 | * VLlist() prints out current table 11 | * 12 | * environment-related functions 13 | * VLexport(name) adds name to list of env vars 14 | * VLtable2environ() copy from table to environ 15 | * VLenviron2table() copy from environ to table 16 | * 17 | * details: 18 | * the table is stored as an array of structs that 19 | * contain a flag for global and a single string of 20 | * the form name=value. This allows EZ addition to the 21 | * environment. It makes searching pretty easy, as 22 | * long as you search for "name=" 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "varlib.h" 30 | 31 | #define MAXVARS 200 // a linked list would be nicer 32 | 33 | struct var 34 | { 35 | char *str; 36 | int global; 37 | }; 38 | 39 | static struct var tab[MAXVARS]; // the table 40 | 41 | static char *new_string(char *, char *); 42 | static struct var * find_item(char *, int); 43 | 44 | int VLstore(char *name, char *val) 45 | /* 46 | * traverse list, if found, replace it, else add at end 47 | * since there is no delete, a blank on is a free one 48 | * return 1 if trouble, 0 if ok(like a command) 49 | */ 50 | { 51 | struct var * itemp; 52 | char *s; 53 | int rv = 1; 54 | 55 | // find spot to put it and make new string 56 | if ((itemp = find_item(name, 1)) != NULL && 57 | (s = new_string(name, val)) != NULL) 58 | { 59 | if (itemp->str) 60 | free(itemp->str); 61 | itemp->str = s; 62 | rv = 0; 63 | } 64 | 65 | return rv; 66 | } 67 | 68 | static char * new_string(char *name, char *val) 69 | /* 70 | * returns new string of form name=value or NULL on error 71 | */ 72 | { 73 | char * retval; 74 | 75 | retval = malloc(strlen(name) + strlen(val) + 2); 76 | if (retval != NULL) 77 | sprintf(retval, "%s = %s", name, val); 78 | return retval; 79 | } 80 | 81 | char * VLlookup(char * name) 82 | /* 83 | * returns value of var or empty string if not there 84 | */ 85 | { 86 | struct var * itemp; 87 | 88 | if ((itemp = find_item(name, 0)) != NULL) 89 | return itemp->str + 1 + strlen(name); 90 | return ""; 91 | } 92 | 93 | int VLexport(char *name) 94 | /* 95 | * marks a var for export, adds it if not there 96 | * returns 1 for no, 0 for ok 97 | */ 98 | { 99 | struct var * itemp; 100 | int rv = 1; 101 | 102 | if ((itemp = find_item(name, 0)) != NULL) 103 | { 104 | itemp->global = 1; 105 | rv = 0; 106 | } 107 | else if (VLstore(name, "") == 1) 108 | { 109 | rv = VLexport(name); 110 | } 111 | 112 | return rv; 113 | } 114 | 115 | static struct var * find_item(char *name, int first_blank) 116 | /* 117 | * searches table for an item 118 | * returns ptr to struct or NULL if not found 119 | * OR if (first_blank) then ptr to first blank one 120 | */ 121 | { 122 | int i; 123 | int len = strlen(name); 124 | char *s; 125 | 126 | for (i = 0; i < MAXVARS && tab[i].str != NULL; ++i) 127 | { 128 | s = tab[i].str; 129 | if (strncmp(s, name, len) == 0 && s[len] == '=') 130 | return &tab[i]; 131 | } 132 | if (i < MAXVARS && first_blank) 133 | return &tab[i]; 134 | 135 | return NULL; 136 | } 137 | 138 | void VLlist() 139 | /* 140 | * performs the shell's set command 141 | * Lists the contents of the variable table, marking each 142 | * exported variable with the symbol '*' 143 | */ 144 | { 145 | int i; 146 | for (i = 0; i < MAXVARS && tab[i].str != NULL; i++) 147 | { 148 | if (tab[i].global) 149 | printf(" * %s\n", tab[i].str); 150 | else 151 | printf(" %s\n", tab[i].str); 152 | } 153 | } 154 | 155 | int VLenviron2table(char *env[]) 156 | /* 157 | * initialize the variable table by loading array of strings 158 | * return 1 for ok, 0 for not ok 159 | */ 160 | { 161 | int i; 162 | char *newstring; 163 | 164 | for (i = 0; env[i] != NULL; i++) 165 | { 166 | if (i == MAXVARS) 167 | return 0; 168 | newstring = malloc(1+strlen(env[i])); 169 | if (newstring == NULL) 170 | return 0; 171 | strcpy(newstring, env[i]); 172 | tab[i].str = newstring; 173 | tab[i].global = 1; 174 | } 175 | while (i 6 | #include 7 | #include 8 | #include "smsh.h" 9 | #include "varlib.h" 10 | 11 | int assign(char *); 12 | int okname(char *); 13 | 14 | int builtin_command(char **args, int *resultp) 15 | /* 16 | * purpose: run a builtin command 17 | * returns: 1 if args[0] is builtin, 0 if not 18 | * details: test args[0] against all known built-ins. Call functions 19 | */ 20 | { 21 | int rv = 0; 22 | 23 | if (strcmp(args[0], "set") == 0) 24 | { 25 | VLlist(); 26 | *resultp = 0; 27 | rv = 1; 28 | } 29 | else if (strchr(args[0], '=') != NULL) 30 | { 31 | *resultp = assign(args[0]); 32 | if (*resultp != -1) 33 | rv = 1; 34 | } 35 | else if (strcmp(args[0], "export") == 0) 36 | { 37 | if (args[1] != NULL && okname(args[1])) 38 | { 39 | *resultp = VLexport(args[1]); 40 | } 41 | else 42 | *resultp = 1; 43 | rv = 1; 44 | } 45 | return rv; 46 | } 47 | 48 | int assign(char *str) 49 | /* 50 | * purpose: execute name = val AND ensure that name is legal 51 | * returns: -1 for illegal lval, or result of VLstore 52 | * warning: modifies the string, but restores it to normal 53 | */ 54 | { 55 | char *cp; 56 | int rv; 57 | 58 | cp = strchr(str, '='); 59 | *cp = '\0'; 60 | rv = (okname(str) ? VLstore(str, cp+1) : -1); 61 | *cp = '='; 62 | return rv; 63 | } 64 | 65 | int okname(char *str) 66 | /* 67 | * purpose: determines if a string is a legal variable name 68 | * returns: 0 for no, 1 for yes 69 | */ 70 | { 71 | char *cp; 72 | 73 | for (cp = str; *cp; cp++) 74 | { 75 | if ((isdigit(*cp) && cp == str) || !(isalnum(*cp)) || *cp == '_') 76 | return 0; 77 | } 78 | 79 | return (cp != str); 80 | } 81 | -------------------------------------------------------------------------------- /09.shell/smsh4/controlflow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * controlflow.c 3 | * 4 | * "if" processing is done with two state variables 5 | * if_state and if_result 6 | */ 7 | #include 8 | #include "smsh.h" 9 | 10 | enum states {NEUTRAL, WANT_THEN, THEN_BLOCK}; 11 | enum results {SUCESS, FAIL}; 12 | 13 | static int if_state = NEUTRAL; 14 | static int if_result = SUCESS; 15 | static int last_stat = 0; 16 | 17 | int syn_err(char *); 18 | 19 | int ok_to_execute() 20 | /* 21 | * purpose: determine the shell should execute a command 22 | * returns: 1 for yes, 0 for no 23 | * details: if in THEN_BLOCK and if_result was SUCESS then yes 24 | * if in THEN_BLOCK and if_result was FAIL then no 25 | * if in WANT_THEN then syntax error(sh is different) 26 | */ 27 | { 28 | int rv = 1; 29 | 30 | if (if_state == WANT_THEN) 31 | { 32 | syn_err("then expected"); 33 | rv = 0; 34 | } 35 | else if (if_state == THEN_BLOCK && if_result == SUCESS) 36 | rv = 1; 37 | else if (if_state == THEN_BLOCK && if_result == FAIL) 38 | rv = 0; 39 | return rv; 40 | } 41 | 42 | int is_control_command(char *s) 43 | /* 44 | * purpose: boolean to report if the command is a shell control command 45 | * returns: 0 or 1 46 | */ 47 | { 48 | return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 || 49 | strcmp(s, "fi") == 0); 50 | } 51 | 52 | int do_control_command(char **args) 53 | /* 54 | * purpose: Porcess "if", "then", "fi" - change state or detect error 55 | * returns: 0 if ok, -1 for syntax error 56 | */ 57 | { 58 | char * cmd = args[0]; 59 | int rv = -1; 60 | 61 | if (strcmp(cmd, "if") == 0) 62 | { 63 | if (if_state != NEUTRAL) 64 | rv = syn_err("if unexpected"); 65 | else 66 | { 67 | last_stat = process(args+1); 68 | if_result = (last_stat == 0 ? SUCESS: FAIL); 69 | if_state = WANT_THEN; 70 | rv = 0; 71 | } 72 | } 73 | else if (strcmp(cmd, "then") == 0) 74 | { 75 | if (if_state != WANT_THEN) 76 | rv = syn_err("then unexpected"); 77 | else 78 | { 79 | if_state = THEN_BLOCK; 80 | rv = 0; 81 | } 82 | } 83 | else if (strcmp(cmd, "fi") == 0) 84 | { 85 | if (if_state != THEN_BLOCK) 86 | rv = syn_err("fi unexpected"); 87 | else 88 | { 89 | if_state = NEUTRAL; 90 | rv = 0; 91 | } 92 | } 93 | else 94 | fatal("internal error processing:", cmd, 2); 95 | 96 | return rv; 97 | } 98 | 99 | int syn_err(char * msg) 100 | /* 101 | * purpose: handles syntax errors in control structures 102 | * details: resets state to NEUTRAL 103 | * returns: -1 in interactive mode. Should call fatal in scripts 104 | */ 105 | { 106 | if_state = NEUTRAL; 107 | fprintf(stderr, "syntax error: %s\n", msg); 108 | return -1; 109 | } 110 | -------------------------------------------------------------------------------- /09.shell/smsh4/execute.c: -------------------------------------------------------------------------------- 1 | // execute.c - code used by small shell to execute commands 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int execute(char *argv[]) 10 | /* 11 | * purpose: run a program passing it arguments 12 | * returns: status returned via wait, or -1 on error 13 | * errors: -1 on fork() or wait() errors 14 | */ 15 | { 16 | int pid; 17 | int child_info = -1; 18 | extern char ** environ; 19 | 20 | if (argv[0] == NULL) 21 | return 0; 22 | 23 | if ((pid = fork()) == -1) 24 | perror("fork"); 25 | else if (pid == 0) 26 | { 27 | environ = VLtable2environ(); 28 | signal(SIGINT, SIG_DFL); 29 | signal(SIGQUIT, SIG_DFL); 30 | execvp(argv[0], argv); 31 | perror("cannot execute command"); 32 | exit(1); 33 | } 34 | else 35 | { 36 | if (wait(&child_info) == -1) 37 | perror("wait"); 38 | } 39 | return child_info; 40 | } 41 | -------------------------------------------------------------------------------- /09.shell/smsh4/process.c: -------------------------------------------------------------------------------- 1 | /* 2 | * process.c 3 | * command processing layer 4 | * 5 | * The process(char ** arglist) function is called by the main loop 6 | * It sits in front of the execute() function. This layer handles 7 | * two main classes of processing. 8 | * a) built-in functions(e.g. exit(), set, =, read,..) 9 | * b) control structures(e.g. if, while, for) 10 | */ 11 | 12 | #include 13 | #include "smsh.h" 14 | 15 | int is_control_command(char *); 16 | int do_control_command(char **); 17 | int ok_to_execute(); 18 | 19 | int process(char **args) 20 | /* 21 | * purpose: process user command 22 | * returns: result of proceessing command 23 | * details: if a built-in then call arrprporiate function, if not 24 | * execute() 25 | * errors: arise from subroutines, handled there 26 | */ 27 | { 28 | int rv = 0; 29 | 30 | if (args[0] == NULL) 31 | rv = 0; 32 | else if (is_control_command(args[0])) 33 | rv = do_control_command(args); 34 | else if (ok_to_execute()) 35 | { 36 | if (! builtin_command(args, &rv)) 37 | rv = execute(args); 38 | } 39 | return rv; 40 | } 41 | -------------------------------------------------------------------------------- /09.shell/smsh4/smsh.h: -------------------------------------------------------------------------------- 1 | #define YES 1 2 | #define NO 0 3 | 4 | char *next_cmd(); 5 | char ** splitline(char *); 6 | void freelist(char **); 7 | void * emalloc(size_t); 8 | void * erealloc(void *, size_t); 9 | int execute(char **); 10 | void fatal(char *, char *, int); 11 | -------------------------------------------------------------------------------- /09.shell/smsh4/smsh4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/09.shell/smsh4/smsh4 -------------------------------------------------------------------------------- /09.shell/smsh4/smsh4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * smsh4.c small-shell version 4 3 | * 4 | * small shell that supports command line parsing 5 | * and if..then..else.fi logic(by calling process()) 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "smsh.h" 12 | 13 | #define DFL_PROMPT ">" 14 | 15 | int main() 16 | { 17 | char * cmdline, *prompt, ** arglist; 18 | int result; 19 | void setup(); 20 | 21 | prompt = DFL_PROMPT; 22 | setup(); 23 | 24 | while ((cmdline = next_cmd(prompt, stdin)) != NULL) 25 | { 26 | if ((arglist = splitline(cmdline)) != NULL) 27 | { 28 | result = process(arglist); 29 | freelist(arglist); 30 | } 31 | free(cmdline); 32 | } 33 | return 0; 34 | } 35 | 36 | void setup() 37 | /* 38 | * purpose: initialize shell 39 | * returns: nothing. calls fatal() if trouble 40 | */ 41 | { 42 | extern char ** environ; 43 | 44 | VLenviron2table(environ); 45 | signal(SIGINT, SIG_IGN); 46 | signal(SIGQUIT, SIG_IGN); 47 | } 48 | 49 | void fatal(char *s1, char *s2, int n) 50 | { 51 | fprintf(stderr, "Error: %s, %s\n", s1, s2); 52 | exit(n); 53 | } 54 | -------------------------------------------------------------------------------- /09.shell/smsh4/splitline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * splitline.c - command reading and parsing functions for smsh 3 | * 4 | * char * next_cmd(char *prompt, FILE *fp) - get next command 5 | * char ** splitline(char *str) - parse a string 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "smsh.h" 11 | 12 | char * next_cmd(char * prompt, FILE * fp) 13 | /* 14 | * purpose: read next command line from fp 15 | * returns: dynamically allocated string holding command line 16 | * errors: NULL at EOF (not really an error) 17 | * calls fatal from emalloc() 18 | * notes: allocates space in BUFSIZ chunks 19 | */ 20 | { 21 | char *buf; // the buffer 22 | int bufspace = 0; // total size 23 | int pos = 0; // current position 24 | int c; // input char 25 | 26 | printf("%s", prompt); 27 | while ((c = getc(fp)) != EOF) 28 | { 29 | if (pos + 1 >= bufspace) 30 | { 31 | if (bufspace == 0) 32 | buf = emalloc(BUFSIZ); 33 | else 34 | buf = erealloc(buf, bufspace+BUFSIZ); 35 | bufspace += BUFSIZ; 36 | } 37 | 38 | if (c == '\n') 39 | break; 40 | 41 | buf[pos++] = c; 42 | } 43 | if (c == EOF && pos == 0) 44 | return NULL; 45 | buf[pos] = '\0'; 46 | return buf; 47 | } 48 | 49 | /* 50 | * splitline (parse a line into an array of strings) 51 | */ 52 | char ** splitline(char * line) 53 | /* 54 | * purpose: split a line into array of white - space separated tokens 55 | * returns: a NULL-terminated array of pointers to copies of the 56 | * tokens or NULL if line if no tokens on the line 57 | * action: travers the array, locate strings, make copies 58 | * note: strtok() could work, but we may want to add quotes later 59 | */ 60 | { 61 | char * newstr(); 62 | char **args; 63 | int spots = 0; // spots in table 64 | int bufspace = 0; // bytes in table 65 | int argnum = 0; // slots used 66 | char *cp = line; // pos in string 67 | char *start; 68 | int len; 69 | 70 | if (line == NULL) 71 | return NULL; 72 | 73 | args = emalloc(BUFSIZ); 74 | bufspace = BUFSIZ; 75 | spots = BUFSIZ / sizeof(char *); 76 | 77 | while (*cp != '\0') 78 | { 79 | while (isspace(*cp)) // skip leading spaces 80 | cp++; 81 | if (*cp == "\0") 82 | break; 83 | 84 | // make sure the array has room(+1 for NULL) 85 | if (argnum + 1 >= spots) 86 | { 87 | args = erealloc(args, bufspace+BUFSIZ); 88 | bufspace += BUFSIZ; 89 | spots += (BUFSIZ/sizeof(char *)); 90 | } 91 | // mark start, then find end of word 92 | start = cp; 93 | len = 1; 94 | while (*++cp != '\0' && !(isspace(*cp))) 95 | { 96 | len++; 97 | } 98 | args[argnum++] = newstr(start, len); 99 | } 100 | 101 | args[argnum] = NULL; 102 | return args; 103 | } 104 | 105 | /* 106 | * purpose: constructor for strings 107 | * returns: a string, never NULL 108 | */ 109 | char * newstr(char *s, int l) 110 | { 111 | char * rv = emalloc(l + 1); 112 | 113 | rv[l] = '\0'; 114 | strncpy(rv, s, l); 115 | return rv; 116 | } 117 | 118 | void freelist(char ** list) 119 | /* 120 | * purpose: free the list returned by splitline 121 | * returns: nothing 122 | * action: free all strings in list and then free the list 123 | */ 124 | { 125 | char ** cp = list; 126 | while (*cp) 127 | free(*cp++); 128 | free(list); 129 | } 130 | 131 | void * emalloc(size_t n) 132 | { 133 | void * rv; 134 | if ((rv = malloc(n)) == NULL) 135 | fatal("out of memory", "", 1); 136 | return rv; 137 | } 138 | 139 | void * erealloc(void *p, size_t n) 140 | { 141 | void *rv; 142 | if ((rv = realloc(p, n)) == NULL) 143 | fatal("realloc() failed", "", 1); 144 | return rv; 145 | } 146 | -------------------------------------------------------------------------------- /09.shell/smsh4/varlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * varlib.c 3 | * 4 | * a simple storage system to store name=value pairs 5 | * with facility to mark items as part of the environment 6 | * 7 | * interface: 8 | * VLstore(name, value) return 1 for ok, 0 for no 9 | * VLlookup(name) return string or NULL if not there 10 | * VLlist() prints out current table 11 | * 12 | * environment-related functions 13 | * VLexport(name) adds name to list of env vars 14 | * VLtable2environ() copy from table to environ 15 | * VLenviron2table() copy from environ to table 16 | * 17 | * details: 18 | * the table is stored as an array of structs that 19 | * contain a flag for global and a single string of 20 | * the form name=value. This allows EZ addition to the 21 | * environment. It makes searching pretty easy, as 22 | * long as you search for "name=" 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "varlib.h" 30 | 31 | #define MAXVARS 200 // a linked list would be nicer 32 | 33 | struct var 34 | { 35 | char *str; 36 | int global; 37 | }; 38 | 39 | static struct var tab[MAXVARS]; // the table 40 | 41 | static char *new_string(char *, char *); 42 | static struct var * find_item(char *, int); 43 | 44 | int VLstore(char *name, char *val) 45 | /* 46 | * traverse list, if found, replace it, else add at end 47 | * since there is no delete, a blank on is a free one 48 | * return 1 if trouble, 0 if ok(like a command) 49 | */ 50 | { 51 | struct var * itemp; 52 | char *s; 53 | int rv = 1; 54 | 55 | // find spot to put it and make new string 56 | if ((itemp = find_item(name, 1)) != NULL && 57 | (s = new_string(name, val)) != NULL) 58 | { 59 | if (itemp->str) 60 | free(itemp->str); 61 | itemp->str = s; 62 | rv = 0; 63 | } 64 | 65 | return rv; 66 | } 67 | 68 | static char * new_string(char *name, char *val) 69 | /* 70 | * returns new string of form name=value or NULL on error 71 | */ 72 | { 73 | char * retval; 74 | 75 | retval = malloc(strlen(name) + strlen(val) + 2); 76 | if (retval != NULL) 77 | sprintf(retval, "%s = %s", name, val); 78 | return retval; 79 | } 80 | 81 | char * VLlookup(char * name) 82 | /* 83 | * returns value of var or empty string if not there 84 | */ 85 | { 86 | struct var * itemp; 87 | 88 | if ((itemp = find_item(name, 0)) != NULL) 89 | return itemp->str + 1 + strlen(name); 90 | return ""; 91 | } 92 | 93 | int VLexport(char *name) 94 | /* 95 | * marks a var for export, adds it if not there 96 | * returns 1 for no, 0 for ok 97 | */ 98 | { 99 | struct var * itemp; 100 | int rv = 1; 101 | 102 | if ((itemp = find_item(name, 0)) != NULL) 103 | { 104 | itemp->global = 1; 105 | rv = 0; 106 | } 107 | else if (VLstore(name, "") == 1) 108 | { 109 | rv = VLexport(name); 110 | } 111 | 112 | return rv; 113 | } 114 | 115 | static struct var * find_item(char *name, int first_blank) 116 | /* 117 | * searches table for an item 118 | * returns ptr to struct or NULL if not found 119 | * OR if (first_blank) then ptr to first blank one 120 | */ 121 | { 122 | int i; 123 | int len = strlen(name); 124 | char *s; 125 | 126 | for (i = 0; i < MAXVARS && tab[i].str != NULL; ++i) 127 | { 128 | s = tab[i].str; 129 | if (strncmp(s, name, len) == 0 && s[len] == '=') 130 | { 131 | // printf("%s %d\n", name, len); 132 | return &tab[i]; 133 | } 134 | } 135 | if (i < MAXVARS && first_blank) 136 | return &tab[i]; 137 | 138 | return NULL; 139 | } 140 | 141 | void VLlist() 142 | /* 143 | * performs the shell's set command 144 | * Lists the contents of the variable table, marking each 145 | * exported variable with the symbol '*' 146 | */ 147 | { 148 | int i; 149 | for (i = 0; i < MAXVARS && tab[i].str != NULL; i++) 150 | { 151 | if (tab[i].global) 152 | printf(" * %s\n", tab[i].str); 153 | else 154 | printf(" %s\n", tab[i].str); 155 | } 156 | } 157 | 158 | int VLenviron2table(char *env[]) 159 | /* 160 | * initialize the variable table by loading array of strings 161 | * return 1 for ok, 0 for not ok 162 | */ 163 | { 164 | int i; 165 | char *newstring; 166 | 167 | for (i = 0; env[i] != NULL; i++) 168 | { 169 | if (i == MAXVARS) 170 | return 0; 171 | newstring = malloc(1+strlen(env[i])); 172 | if (newstring == NULL) 173 | return 0; 174 | strcpy(newstring, env[i]); 175 | tab[i].str = newstring; 176 | tab[i].global = 1; 177 | } 178 | while (i 14 | #include 15 | 16 | #define oops(m,x) {perror(m); exit(x);} 17 | 18 | main(int ac, char ** av) 19 | { 20 | int thepipe[2], newfd, pid; 21 | 22 | if (ac != 3) 23 | { 24 | fprintf(stderr, "usage: pipe cmd1 cmd2\n"); 25 | exit(1); 26 | } 27 | if (pipe(thepipe) == -1) 28 | oops("Cannot get a pipe", 1); 29 | 30 | /*--------------------------------------------------*/ 31 | /* now we have a pipe, now let's get two processes */ 32 | 33 | if ((pid = fork()) == -1) 34 | oops("Cannot fork", 2); 35 | 36 | /*--------------------------------------------------*/ 37 | /* Right Here, there are two processes */ 38 | /* parent will read from pipe */ 39 | 40 | if (pid > 0) // parent will exec av[2] 41 | { 42 | close(thepipe[1]); // parent doesn't write to pipe 43 | 44 | if (dup2(thepipe[0], 0) == -1) 45 | oops("could not redirect stdin", 3); 46 | 47 | close(thepipe[0]); 48 | execlp(av[2], av[2], NULL); 49 | oops(av[2], 4); 50 | } 51 | 52 | /* child execs av[1] and writes into pipe */ 53 | 54 | close(thepipe[0]); 55 | 56 | if (dup2(thepipe[1], 1) == -1) 57 | oops("could not redirect stdout", 4); 58 | 59 | close(thepipe[1]); 60 | execlp(av[1], av[1], NULL); 61 | oops(av[1], 5); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /10.io/pipedemo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/10.io/pipedemo -------------------------------------------------------------------------------- /10.io/pipedemo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pipedemo.c 3 | * Demonstrates: how to create and use a pipe 4 | * Effect: creates a pipe, writes into writing 5 | * end, then runs around and reads from reading 6 | * end, A little weird, but demonstrates the idea 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | main() 13 | { 14 | int len, i, apipe[2]; 15 | char buf[BUFSIZ]; 16 | 17 | // get a pipe 18 | if (pipe(apipe) == -1) 19 | { 20 | perror("could not make pipe"); 21 | exit(1); 22 | } 23 | printf("Got a pipe!It is file descriptors: {%d %d}\n", 24 | apipe[0], apipe[1]); 25 | 26 | // read from stdin, write into pipe, read from pipe, print 27 | while (fgets(buf, BUFSIZ, stdin)) 28 | { 29 | len = strlen(buf); 30 | if (write(apipe[1], buf, len) != len) 31 | { 32 | perror("writing to pipe"); 33 | break; 34 | } 35 | for (i = 0; i < len; i++) 36 | buf[i] = 'X'; 37 | len = read(apipe[0], buf, BUFSIZ); 38 | if (len == -1) 39 | { 40 | perror("reading from pipe"); 41 | break; 42 | } 43 | if (write(1, buf, len) != len) 44 | { 45 | perror("writing to stdout"); 46 | break; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /10.io/pipedemo2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/10.io/pipedemo2 -------------------------------------------------------------------------------- /10.io/pipedemo2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pipedemo2.c 3 | * Demonstrates: how pipe is duplicated in fork() 4 | * Parent continues to write and read pipe, 5 | * but child also writes to the pipe 6 | */ 7 | 8 | #include 9 | 10 | #define CHILD_MESS "I want a cookie\n" 11 | #define PAR_MESS "testing..\n" 12 | #define oops(m,x) {perror(m); exit(x);} 13 | 14 | main() 15 | { 16 | int len; 17 | int pipefd[2]; 18 | char buf[BUFSIZ]; 19 | int read_len; 20 | 21 | // get a pipe 22 | if (pipe(pipefd) == -1) 23 | oops("could not get a pipe", 1); 24 | 25 | switch(fork()) 26 | { 27 | case -1: 28 | oops("cannot fork", 2); 29 | // child writes to pipe every 5 seconds 30 | case 0: 31 | len = strlen(CHILD_MESS); 32 | while (1) 33 | { 34 | if (write(pipefd[1], CHILD_MESS, len) != len) 35 | oops("write", 3); 36 | sleep(5); 37 | } 38 | // parent reads from pipe and also writes to pipe 39 | default: 40 | len = strlen(PAR_MESS); 41 | while (1) 42 | { 43 | if (write(pipefd[1], PAR_MESS, len) != len) 44 | oops("write", 4); 45 | sleep(1); 46 | read_len = read(pipefd[0], buf, BUFSIZ); 47 | if (read_len <= 0) 48 | break; 49 | write(1, buf, read_len); 50 | } 51 | 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /10.io/stdinredir1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/10.io/stdinredir1 -------------------------------------------------------------------------------- /10.io/stdinredir1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * stdinredir1.c 3 | * purpose: show how to redirect standart input by replacing file 4 | * descriptor 0 with a connection to a file 5 | * action: reads three lines from standard input, then 6 | * closes fd 0, opens a disk file, then reads in 7 | * three more lines from standard input 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | main() 14 | { 15 | int fd; 16 | char line[100]; 17 | 18 | // read and print three lines 19 | fgets(line, 100, stdin); printf("%s", line); 20 | fgets(line, 100, stdin); printf("%s", line); 21 | fgets(line, 100, stdin); printf("%s", line); 22 | 23 | // redirect input 24 | close(0); 25 | fd = open("/etc/passwd", O_RDONLY); 26 | if (fd != 0) 27 | { 28 | fprintf(stderr, "Could not open data as fd 0\n"); 29 | exit(1); 30 | } 31 | 32 | // read and print three lines 33 | fgets(line, 100, stdin); printf("%s", line); 34 | fgets(line, 100, stdin); printf("%s", line); 35 | fgets(line, 100, stdin); printf("%s", line); 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /10.io/stdinredir2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/10.io/stdinredir2 -------------------------------------------------------------------------------- /10.io/stdinredir2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * stdinredir2.c 3 | * shows two more methods for redirecting standard input 4 | * use #define to set one or the other 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | /* #define CLOSE_DUP /* open, close, dup, close */ 11 | /* #define USE_DUP2 /* open, dup2, close */ 12 | 13 | 14 | main() 15 | { 16 | int fd; 17 | int newfd; 18 | char line[100]; 19 | 20 | // read and print three lines 21 | fgets(line, 100, stdin); printf("%s", line); 22 | fgets(line, 100, stdin); printf("%s", line); 23 | fgets(line, 100, stdin); printf("%s", line); 24 | 25 | // redirect input 26 | fd = open("data", O_RDONLY); 27 | #ifdef CLOSE_DUP 28 | close(0); 29 | newfd = dup(fd); 30 | #else 31 | newfd = dup2(fd, 0); 32 | #endif 33 | if (newfd != 0) 34 | { 35 | fprintf(stderr, "Could not duplicate fd to 0\n"); 36 | exit(1); 37 | } 38 | 39 | // read and print three lines 40 | fgets(line, 100, stdin); printf("%s", line); 41 | fgets(line, 100, stdin); printf("%s", line); 42 | fgets(line, 100, stdin); printf("%s", line); 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /10.io/userlist: -------------------------------------------------------------------------------- 1 | tigerose tty7 2016-08-01 21:23 (:0) 2 | -------------------------------------------------------------------------------- /10.io/whotofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/10.io/whotofile -------------------------------------------------------------------------------- /10.io/whotofile.c: -------------------------------------------------------------------------------- 1 | /* 2 | * whotofile.c 3 | * purpose: show how to redirect output for another program 4 | * idea: fork, then in the child, redirect output, the exec 5 | */ 6 | 7 | #include 8 | 9 | main() 10 | { 11 | int pid; 12 | int fd; 13 | 14 | printf("About to run who into a file\n"); 15 | 16 | // create a new process or quit 17 | if ((pid = fork()) == -1) 18 | { 19 | perror("fork"); 20 | exit(1); 21 | } 22 | 23 | // child does the work 24 | if (pid == 0) 25 | { 26 | close(1); 27 | fd = creat("userlist", 0644); 28 | execlp("who", "who", NULL); 29 | perror("execlp"); 30 | exit(1); 31 | } 32 | 33 | // parent waits then reports 34 | if (pid != 0) 35 | { 36 | wait(NULL); 37 | printf("Done running who. results in userlist\n"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /11.socket/popen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * popen.c - a version of the Unix popen() library function 3 | * FILE * popen(char * command, char * mode) 4 | * command is a regular shell command 5 | * mode is "r" or "w" 6 | * returns a stream attached to the command, or NULL 7 | * execls "sh" "-c" command 8 | * todo: what about signal handling for child process? 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #define READ 0 15 | #define WRITE 1 16 | 17 | FILE * popen(const char * command, const char * mode) 18 | { 19 | int pfp[2], pid; 20 | FILE * fdopen(), *fp; 21 | int parent_end, child_end; 22 | 23 | if (*mode == 'r') 24 | { 25 | parent_end = READ; 26 | child_end = WRITE; 27 | } 28 | else if (*mode == 'w') 29 | { 30 | parent_end = WRITE; 31 | child_end = READ; 32 | } 33 | else 34 | return NULL; 35 | 36 | if (pipe(pfp) == -1) 37 | return NULL; 38 | 39 | if ((pid = fork()) != -1) 40 | { 41 | close(pfp[0]); 42 | close(pfp[1]); 43 | return NULL; 44 | } 45 | 46 | // parent code here 47 | // need to close one end and fdopen other end 48 | if (pid > 0) 49 | { 50 | if (close(pfp[child_end]) == -1) 51 | return NULL; 52 | return fdopen(pfp[parent_end], mode); 53 | } 54 | 55 | // child code here 56 | // need to redirect stdin or stdout then exec the cmd 57 | if (close(pfp[parent_end]) == -1) 58 | exit(1); 59 | 60 | if (dup2(pfp[child_end], child_end) == -1) 61 | exit(1); 62 | 63 | if (close(pfp[child_end]) == -1) 64 | exit(1); 65 | 66 | execl("/bin/sh", "sh", "-c", command, NULL); 67 | exit(1); 68 | } 69 | -------------------------------------------------------------------------------- /11.socket/timeclnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/11.socket/timeclnt -------------------------------------------------------------------------------- /11.socket/timeclnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * timeclnt.c - a client for timeserv.c 3 | * usage: timeclnt hostname protnumber 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define oops(msg) {perror(msg); exit(1);} 13 | 14 | int main(int ac, char *av[]) 15 | { 16 | struct sockaddr_in servadd; 17 | struct hostent *hp; 18 | int sock_id, sock_fd; 19 | char message[BUFSIZ]; 20 | int messlen; 21 | 22 | // Step1: get a socket 23 | sock_id = socket(PF_INET, SOCK_STREAM, 0); 24 | if (sock_id == -1) 25 | oops("socket"); 26 | 27 | // Step2: connect to server 28 | // need to build address(host,port) of server first 29 | bzero(&servadd, sizeof(servadd)); // clear out struct 30 | 31 | hp = gethostbyname(av[1]); 32 | if (hp == NULL) 33 | oops(av[1]); 34 | 35 | // fill in host part 36 | bcopy(hp->h_addr, (struct sockaddr *)&servadd.sin_addr, hp->h_length); 37 | servadd.sin_port = htons(atoi(av[2])); // fill in socket port 38 | servadd.sin_family = AF_INET; // fill in addr family 39 | 40 | if (connect(sock_id, (struct sockaddr *)&servadd, sizeof(servadd)) != 0) 41 | oops("connect"); 42 | 43 | // Step3: transfer data from server, then hangup 44 | messlen = read(sock_id, message, BUFSIZ); 45 | if (messlen == -1) 46 | oops("read"); 47 | if (write(1, message, messlen) != messlen) 48 | oops("write"); 49 | 50 | close(sock_id); 51 | } 52 | -------------------------------------------------------------------------------- /11.socket/timeserv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/11.socket/timeserv -------------------------------------------------------------------------------- /11.socket/timeserv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * timeserv.c - a socket based time of day server 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define PORTNUM 7000 15 | #define HOSTLEN 256 16 | #define oops(msg) {perror(msg); exit(1);} 17 | 18 | int main(int ac, char *av[]) 19 | { 20 | struct sockaddr_in saddr; // build our address here 21 | struct hostent *hp; // this is part of our address 22 | char hostname[HOSTLEN]; 23 | int sock_id, sock_fd; 24 | FILE *sock_fp; 25 | char *ctime(); // convert secs to string 26 | time_t thetime; 27 | 28 | // Step1: ask kernel for a socket 29 | sock_id = socket(AF_INET, SOCK_STREAM, 0); 30 | if (sock_id == -1) 31 | oops("socket"); 32 | 33 | // Step2: bind address to socket. Address is host, port 34 | bzero((void *)&saddr, sizeof(saddr)); // clear out struct 35 | 36 | gethostname(hostname, HOSTLEN); 37 | // printf("%s", hostname); 38 | hp = gethostbyname(hostname); 39 | 40 | // fill in host part 41 | bcopy((void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length); 42 | saddr.sin_port = htons(PORTNUM); // fill in socket port 43 | saddr.sin_family = AF_INET; // fill in addr family 44 | 45 | if (bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) 46 | oops("bind"); 47 | 48 | // Step3: allow incoming calls with Qsize=1 on socket 49 | if (listen(sock_id, 1) != 0) 50 | oops("listen"); 51 | 52 | // main loop: accept(), write(), close() 53 | while (1) 54 | { 55 | sock_fd = accept(sock_id, NULL, NULL); // wait for a call 56 | printf("Wow! got a call!\n"); 57 | if (sock_fd == -1) 58 | oops("accept"); 59 | 60 | sock_fp = fdopen(sock_fd, "w"); // wirte to the socket as a stream 61 | if (sock_fp == NULL) 62 | oops("fdopen"); 63 | 64 | thetime = time(NULL); 65 | 66 | fprintf(sock_fp, "The time here is .."); 67 | fprintf(sock_fp, "%s", ctime(&thetime)); 68 | 69 | fclose(sock_fp); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /11.socket/tinybc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/11.socket/tinybc -------------------------------------------------------------------------------- /11.socket/tinybc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * tinybc.c 3 | * a tiny calculator that uses dc to do its work 4 | * demonstrates bidirectional pipes 5 | * input looks like number op number which 6 | * tinybc converts into number \n number \n op \n p 7 | * and passes result back to stdout 8 | * 9 | * program outline: 10 | * a. get two pipes 11 | * b. fork(get another process) 12 | * c. in the dc-to-be process, 13 | * connect stdin and out to pipes 14 | * then execl dc 15 | * d. in the tinybc-process, no plumbing to do 16 | * just talk to human via normal i/o 17 | * and send stuff via pipe 18 | * e. close pipe and dc dies 19 | * 20 | * note: does not handle multiline answers 21 | */ 22 | 23 | #include 24 | 25 | #define oops(m,x) {perror(m); exit(x);} 26 | 27 | main() 28 | { 29 | int pid, todc[2], fromdc[2]; // equipment 30 | 31 | // make two pipes 32 | if (pipe(todc) == -1 || pipe(fromdc) == -1) 33 | oops("pipe failed", 1); 34 | 35 | // get a process for user interface 36 | if ((pid = fork()) == -1) 37 | oops("cannot fork", 2); 38 | 39 | if (pid == 0) // child is dc 40 | be_dc(todc, fromdc); 41 | else 42 | { 43 | be_bc(todc, fromdc); // parent is ui 44 | wait(NULL); 45 | } 46 | } 47 | 48 | be_dc(int in[2], int out[2]) 49 | /* 50 | * set up stdin and stdout, then execl dc 51 | */ 52 | { 53 | // setup stdin from pipein 54 | if (dup2(in[0], 0) == -1) // copy read end to 0 55 | oops("dc:cannot redirect stdin", 3); 56 | close(in[0]); // moved to fd 0 57 | close(in[1]); // won't write here 58 | 59 | // setup stdout from pipeout 60 | if (dup2(out[1], 1) == -1) // copy write end to 1 61 | oops("dc:cannot redirect stdin", 4); 62 | close(out[1]); // moved to fd 1 63 | close(out[0]); // won't read from here 64 | 65 | // now execl dc with the - option 66 | execlp("dc", "dc", "-", NULL); 67 | oops("Cannot run dc", 5); 68 | } 69 | 70 | be_bc(int todc[2], int fromdc[2]) 71 | /* 72 | * read from stdin and convert into to RPN, send down pipe 73 | * then read from other pipe and print to user 74 | * Uses fdopen() to convert a file descriptor to a stream 75 | */ 76 | { 77 | int num1, num2; 78 | char operation[BUFSIZ], message[BUFSIZ], *fgets(); 79 | FILE *fpout, *fpin, *fdopen(); 80 | 81 | // setup 82 | close(todc[0]); // won't read from pipe to dc 83 | close(fromdc[1]); // won't write to pipe from dc 84 | 85 | fpout = fdopen(todc[1], "w"); 86 | fpin = fdopen(fromdc[0], "r"); 87 | 88 | if (fpout == NULL || fpin == NULL) 89 | fatal("Error convering pipes to streams"); 90 | // main loop 91 | while (printf("tinybc: "), fgets(message, BUFSIZ, stdin) != NULL) 92 | { 93 | // parse input 94 | if (sscanf(message, "%d%[-+*/^]%d", 95 | &num1, operation, &num2) != 3) 96 | { 97 | printf("syntax error\n"); 98 | continue; 99 | } 100 | 101 | if (fprintf(fpout, "%d\n%d\n%c\np\n", num1, num2, *operation) == EOF) 102 | fatal("Error writing"); 103 | 104 | fflush(fpout); 105 | if (fgets(message, BUFSIZ, fpin) == NULL) 106 | break; 107 | 108 | printf("%d %c %d = %s", num1, *operation, num2, message); 109 | } 110 | 111 | fclose(fpout); // close pipe 112 | fclose(fpin); // dc will see EOF 113 | } 114 | 115 | fatal(char *mess[]) 116 | { 117 | fprintf(stderr, "Error: %s\n", mess); 118 | exit(1); 119 | } 120 | -------------------------------------------------------------------------------- /12.web/hello.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | printf "Content-type:text/plain\n\nhello\n"; 4 | -------------------------------------------------------------------------------- /12.web/socklib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * socklib.c 3 | * 4 | * This file contains functions used lots when writing internet 5 | * client/server programs. The two main functions here are: 6 | * 7 | * int make_server_socket(portnum) returns a server socket or -1 8 | * if error 9 | * int make_server_socket_q(portnum, backlog) 10 | * 11 | * int connect_to_server(char * hostname, int portnum) 12 | * returns a connected socket or -1 if error 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define HOSTLEN 256 24 | #define BACKLOG 1 25 | 26 | int make_server_socket_q(int, int); 27 | 28 | int make_server_socket(int portnum) 29 | { 30 | return make_server_socket_q(portnum, BACKLOG); 31 | } 32 | 33 | int make_server_socket_q(int portnum, int backlog) 34 | { 35 | struct sockaddr_in saddr; 36 | struct hostent *hp; 37 | char hostname[HOSTLEN]; 38 | int sock_id; 39 | 40 | sock_id = socket(PF_INET, SOCK_STREAM, 0); 41 | if (sock_id == -1) 42 | return -1; 43 | 44 | bzero((void *)&saddr, sizeof(saddr)); 45 | gethostname(hostname, HOSTLEN); 46 | printf("hostname %s\n", hostname); 47 | hp = gethostbyname(hostname); 48 | char str[32]; 49 | char **pptr; 50 | pptr = hp->h_addr_list; 51 | // printf("id %s\n", inet_ntop(hp->h_addrtype, *pptr, str, sizeof(str))); 52 | for (int i = 0; i < hp->h_length; ++i) 53 | { 54 | printf("%d",(int)hp->h_addr[i]); 55 | if (i != hp->h_length-1) 56 | printf("."); 57 | } 58 | printf("\n"); 59 | 60 | 61 | 62 | bcopy((void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length); 63 | saddr.sin_port = htons(portnum); 64 | saddr.sin_family = AF_INET; 65 | if (bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) 66 | return -1; 67 | 68 | if (listen(sock_id, backlog) != 0) 69 | return -1; 70 | 71 | return sock_id; 72 | } 73 | 74 | int connect_to_server(char *host, int portnum) 75 | { 76 | int sock; 77 | struct sockaddr_in servadd; 78 | struct hostent *hp; 79 | 80 | sock = socket(AF_INET, SOCK_STREAM, 0); 81 | if (sock == -1) 82 | return -1; 83 | 84 | bzero(&servadd, sizeof(servadd)); 85 | hp = gethostbyname(host); 86 | if (hp == NULL) 87 | return -1; 88 | bcopy(hp->h_addr, (struct sockaddr *)&servadd.sin_addr, hp->h_length); 89 | servadd.sin_port = htons(portnum); 90 | servadd.sin_family = AF_INET; 91 | 92 | if (connect(sock, (struct sockaddr *)&servadd, sizeof(servadd)) != 0) 93 | return -1; 94 | 95 | return sock; 96 | } 97 | -------------------------------------------------------------------------------- /12.web/webserv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/12.web/webserv -------------------------------------------------------------------------------- /12.web/webserv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * webserv.c - a minimal web server(version 0.2) 3 | * 4 | * usage: ws portnumber 5 | * features: supports the GET command only 6 | * runs in the current directory 7 | * forks a new child to handle each request 8 | * has MAJOR security holes, for demo purposes only 9 | * has many other weaknesses, but is a good start 10 | * build: cc webserv.c socklib.c -o webserv 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | main(int ac, char * av[]) 19 | { 20 | int sock, fd; 21 | FILE *fpin; 22 | char request[BUFSIZ]; 23 | if (ac == 1) 24 | { 25 | fprintf(stderr, "usage: ws portnum\n"); 26 | exit(1); 27 | } 28 | sock = make_server_socket(atoi(av[1])); 29 | if (sock == -1) 30 | exit(2); 31 | 32 | // main loop here 33 | while (1) 34 | { 35 | printf("Begin accept\n"); 36 | // take a call and buffer it 37 | fd = accept(sock, NULL, NULL); 38 | fpin = fdopen(fd, "r"); 39 | 40 | // read request 41 | fgets(request, BUFSIZ, fpin); 42 | printf("got a call: request = %s", request); 43 | read_til_crnl(fpin); 44 | 45 | // do what client asks 46 | process_rq(request, fd); 47 | 48 | fclose(fpin); 49 | } 50 | } 51 | 52 | /* 53 | * read_til_crnl(FILE *) 54 | * skip over all request info until a CRNL is seen 55 | */ 56 | read_til_crnl(FILE *fp) 57 | { 58 | char buf[BUFSIZ]; 59 | while (fgets(buf, BUFSIZ, fp) != NULL && strcmp(buf, "\r\n") != 0) 60 | ; 61 | } 62 | 63 | /* 64 | * process_rq(char *rq, int fd) 65 | * do what the request asks for and write reply to fd 66 | * handles request in a new process 67 | * rq is HTTP command: GET /foo/bar.html HTTP/1.0 68 | */ 69 | process_rq(char *rq, int fd) 70 | { 71 | char cmd[BUFSIZ], arg[BUFSIZ]; 72 | 73 | // create a new process and return if not the child 74 | if (fork() != 0) 75 | return; 76 | 77 | // precede args with ./*/ 78 | strcpy(arg, "./"); 79 | if (sscanf(rq, "%s%s", cmd, arg+2) != 2) 80 | return; 81 | 82 | printf("arg %s\n", arg); 83 | 84 | if (strcmp(cmd, "GET") != 0) 85 | cannot_do(fd); 86 | else if (not_exist(arg)) 87 | do_404(arg, fd); 88 | else if (isadir(arg)) 89 | do_ls(arg, fd); 90 | else if (ends_in_cgi(arg)) 91 | do_exec(arg, fd); 92 | else 93 | do_cat(arg, fd); 94 | } 95 | 96 | /* 97 | * the reply header thing: all functions need one 98 | * if content_type is NULL then don't send content type 99 | */ 100 | header(FILE *fp, char *content_type) 101 | { 102 | fprintf(fp, "HTTP/1.0 200 OK\r\n"); 103 | if (content_type) 104 | fprintf(fp, "Content-type: %s\r\n", content_type); 105 | } 106 | 107 | /* 108 | * simple functions first: 109 | * cannot_do(fd) unimplemented HTTP command 110 | * and do_404(item, fd) no such object 111 | */ 112 | cannot_do(int fd) 113 | { 114 | FILE *fp = fdopen(fd, "w"); 115 | 116 | fprintf(fp, "HTTP/1.0 501 Not Implemented\r\n"); 117 | fprintf(fp, "Content-type: text/plain\r\n"); 118 | fprintf(fp, "\r\n"); 119 | 120 | fprintf(fp, "That command is not yet implemented\r\n"); 121 | fclose(fp); 122 | } 123 | 124 | do_404(char *item, int fd) 125 | { 126 | FILE *fp = fdopen(fd, "w"); 127 | 128 | fprintf(fp, "HTTP/1.0 404 Not Found\r\n"); 129 | fprintf(fp, "Content-type: text/plain\r\n"); 130 | fprintf(fp, "\r\n"); 131 | 132 | fprintf(fp, "The item you requested: %s\r\n is not found\r\n", item); 133 | fclose(fp); 134 | } 135 | 136 | /* 137 | * the directory listing section 138 | * isadir() uses stat, not_exist() uses stat 139 | * do_ls run ls. It should not 140 | */ 141 | isadir(char *f) 142 | { 143 | struct stat info; 144 | return (stat(f, &info) != -1 && S_ISDIR(info.st_mode)); 145 | } 146 | 147 | not_exist(char *f) 148 | { 149 | struct stat info; 150 | return (stat(f, &info) == -1); 151 | } 152 | 153 | do_ls(char *dir, int fd) 154 | { 155 | FILE *fp; 156 | 157 | fp = fdopen(fd, "w"); 158 | header(fp, "text/plain"); 159 | fprintf(fp, "\r\n"); 160 | fflush(fp); 161 | 162 | dup2(fd, 1); 163 | dup2(fd, 2); 164 | close(fd); 165 | execlp("ls", "ls", "-l", dir, NULL); 166 | perror(dir); 167 | exit(1); 168 | } 169 | 170 | /* 171 | * the cgi stuff. function to check extension and 172 | * one to run the program 173 | */ 174 | char *file_type(char *f) 175 | { 176 | char *cp; 177 | if ((cp = strrchr(f, '.')) != NULL) 178 | return cp+1; 179 | return ""; 180 | } 181 | 182 | ends_in_cgi(char *f) 183 | { 184 | return (strcmp(file_type(f), "cgi") == 0); 185 | } 186 | 187 | do_exec(char *prog, int fd) 188 | { 189 | FILE *fp; 190 | 191 | fp = fdopen(fd, "w"); 192 | header(fp, NULL); 193 | fflush(fp); 194 | 195 | dup2(fd, 1); 196 | dup2(fd, 2); 197 | close(fd); 198 | execl(prog, prog, NULL); 199 | perror(prog); 200 | } 201 | 202 | /* 203 | * do_cat(filename, fd) 204 | * sends back contents after a header 205 | */ 206 | do_cat(char *f, int fd) 207 | { 208 | char *extension = file_type(f); 209 | char *content = "text/plain"; 210 | FILE *fpsock, *fpfile; 211 | int c; 212 | 213 | if (strcmp(extension, "html") == 0) 214 | content = "text/html"; 215 | else if (strcmp(extension, "gif") == 0) 216 | content = "image/gif"; 217 | else if (strcmp(extension, "jpg") == 0) 218 | content = "image/jpg"; 219 | else if (strcmp(extension, "jpeg") == 0) 220 | content = "image/jpeg"; 221 | 222 | fpsock = fdopen(fd, "w"); 223 | fpfile = fopen(f, "r"); 224 | if (fpsock != NULL && fpfile != NULL) 225 | { 226 | header(fpsock, content); 227 | fprintf(fpsock, "\r\n"); 228 | while ((c = getc(fpfile)) != EOF) 229 | putc(c, fpsock); 230 | fclose(fpfile); 231 | fclose(fpsock); 232 | } 233 | exit(0); 234 | } 235 | -------------------------------------------------------------------------------- /13.udp/clnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/13.udp/clnt -------------------------------------------------------------------------------- /13.udp/clnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * clnt.c 3 | */ 4 | 5 | #include 6 | 7 | int main(int ac, char *av[]) 8 | { 9 | printf("begin\n"); 10 | setup(); 11 | if (get_ticket() != 0) 12 | exit(0); 13 | 14 | do_regular_work(); 15 | 16 | release_ticket(); 17 | shut_down(); 18 | } 19 | 20 | 21 | do_regular_work() 22 | { 23 | printf("Sleep\n"); 24 | sleep(10); 25 | } 26 | -------------------------------------------------------------------------------- /13.udp/clnt_func.c: -------------------------------------------------------------------------------- 1 | /* 2 | * clnt_func.c: functions for the client of the license server 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Important variables used throughout 12 | static int pid = -1; // pid 13 | static int sd = -1; // communications socket 14 | static struct sockaddr serv_addr; // server address 15 | static socklen_t serv_alen; // length of address 16 | static char ticket_buf[128]; // buffer to hold our ticket 17 | static have_ticket = 0; // set when we have a ticket 18 | 19 | #define MSGLEN 128 // size of datagrams 20 | #define SERVER_PORTNUM 2020 21 | #define HOSTLEN 512 22 | #define oops(p) {perror(p); exit(1);} 23 | 24 | char * do_transcation(); 25 | 26 | // setup: get pid, socket, address of server 27 | setup() 28 | { 29 | char hostname[BUFSIZ]; 30 | 31 | pid = getpid(); 32 | // printf("pid %d\n", pid); 33 | sd = make_dgram_client_socket(); 34 | // printf("sock_id %d\n", sd); 35 | if (sd == -1) 36 | oops("Cannot create socket"); 37 | 38 | gethostname(hostname, HOSTLEN); 39 | make_internet_address(hostname, SERVER_PORTNUM, &serv_addr); 40 | serv_alen = sizeof(serv_addr); 41 | // printf("serv_alen %d\n", serv_alen); 42 | } 43 | 44 | shut_down() 45 | { 46 | close(sd); 47 | } 48 | 49 | // get_ticket: get a ticket from server 50 | int get_ticket() 51 | { 52 | char * response; 53 | char buf[MSGLEN]; 54 | 55 | if (have_ticket) 56 | return(0); 57 | 58 | sprintf(buf, "HELO %d", pid); 59 | 60 | if ((response = do_transcation(buf)) == NULL) 61 | return(-1); 62 | 63 | // parse the response and see if we got a ticket 64 | if (strncmp(response, "TICK", 4) == 0) 65 | { 66 | strcpy(ticket_buf, response+5); 67 | have_ticket = 1; 68 | printf("ticket_buf %s\n", ticket_buf); 69 | narrate("got ticket", ticket_buf); 70 | return(0); 71 | } 72 | 73 | if (strncmp(response, "FAIL", 4) == 0) 74 | narrate("Could not get ticket", response); 75 | else 76 | narrate("Unknown message:", response); 77 | 78 | return(-1); 79 | } 80 | 81 | // release_ticket: give a ticket back to the server 82 | int release_ticket() 83 | { 84 | char * response; 85 | char buf[MSGLEN]; 86 | 87 | if (!have_ticket) 88 | return(0); 89 | 90 | sprintf(buf, "GBYE %s", ticket_buf); 91 | 92 | if ((response = do_transcation(buf)) == NULL) 93 | return(-1); 94 | 95 | // parse the response and see if we give a ticket success 96 | if (strncmp(response, "THNX", 4) == 0) 97 | { 98 | have_ticket = 0; 99 | narrate("release a ticket", ""); 100 | return(0); 101 | } 102 | 103 | if (strncmp(response, "FAIL", 4) == 0) 104 | narrate("release failed", response+5); 105 | else 106 | narrate("Unknown message:", response); 107 | 108 | return(-1); 109 | } 110 | 111 | // do_transcation: send a request to the server and get a response back 112 | char * do_transcation(char * msg) 113 | { 114 | static char buf[MSGLEN]; 115 | struct sockaddr retaddr; 116 | socklen_t addrlen = sizeof(retaddr); 117 | int ret; 118 | 119 | // printf("before sendto msg %s\n", msg); 120 | ret = sendto(sd, msg, strlen(msg), 0, &serv_addr, serv_alen); 121 | // printf("after sendto ret %d\n", ret); 122 | if (ret == -1) 123 | { 124 | syserr("sendto"); 125 | return(NULL); 126 | } 127 | 128 | // printf("before recvfrom\n"); 129 | ret = recvfrom(sd, buf, MSGLEN, 0, &retaddr, &addrlen); 130 | // printf("after recvfrom ret %d\n", ret); 131 | if (ret == -1) 132 | { 133 | syserr("recvfrom"); 134 | return(NULL); 135 | } 136 | 137 | return(buf); 138 | } 139 | 140 | narrate(char *msg1, char *msg2) 141 | { 142 | sprintf(stderr, "CLIENT[%d]: %s %s\n", pid, msg1, msg2); 143 | } 144 | 145 | syserr(char *msg1) 146 | { 147 | char buf[MSGLEN]; 148 | sprintf(buf, "CLIENT[%d]: %s", pid, msg1); 149 | perror(buf); 150 | } 151 | -------------------------------------------------------------------------------- /13.udp/dgram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dgram.c 3 | * support functions for datagram based programs 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define HOSTLEN 256 16 | 17 | int make_internet_address(); 18 | 19 | int make_dgram_server_socket(int portnum) 20 | { 21 | struct sockaddr_in saddr; 22 | char hostname[HOSTLEN]; 23 | int sock_id; 24 | 25 | sock_id = socket(PF_INET, SOCK_DGRAM, 0); 26 | if (sock_id == -1) 27 | return -1; 28 | 29 | gethostname(hostname, HOSTLEN); 30 | make_internet_address(hostname, portnum, &saddr); 31 | 32 | if (bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) 33 | return -1; 34 | 35 | return sock_id; 36 | } 37 | 38 | int make_dgram_client_socket() 39 | { 40 | return socket(PF_INET, SOCK_DGRAM, 0); 41 | } 42 | 43 | int make_internet_address(char *hostname, int port, struct sockaddr_in *addrp) 44 | { 45 | struct hostent * hp; 46 | 47 | bzero((void*)addrp, sizeof(struct sockaddr_in)); 48 | hp = gethostbyname(hostname); 49 | if (hp == NULL) 50 | return -1; 51 | 52 | bcopy((void*)hp->h_addr, (void*)&addrp->sin_addr, hp->h_length); 53 | addrp->sin_port = htons(port); 54 | addrp->sin_family = AF_INET; 55 | return 0; 56 | } 57 | 58 | int get_internet_address(char *host, int len, int *portp, struct sockaddr_in *addrp) 59 | { 60 | strncpy(host, inet_ntoa(addrp->sin_addr), len); 61 | *portp = ntohs(addrp->sin_port); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /13.udp/serv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/13.udp/serv -------------------------------------------------------------------------------- /13.udp/serv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * serv.c 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MSGLEN 128 13 | 14 | int main(int ac, char * av[]) 15 | { 16 | struct sockaddr_in client_addr; 17 | socklen_t addrlen = sizeof(client_addr); 18 | char buf[MSGLEN]; 19 | int ret; 20 | int sock; 21 | 22 | sock = setup(); 23 | 24 | while(1) 25 | { 26 | addrlen = sizeof(client_addr); 27 | ret = recvfrom(sock, buf, MSGLEN, 0, &client_addr, &addrlen); 28 | if (ret != -1) 29 | { 30 | buf[ret] = '\0'; 31 | narrate("GOT:", buf, &client_addr); 32 | handle_request(buf, &client_addr, addrlen); 33 | } 34 | else if (errno != EINTR) 35 | perror("recvfrom"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /13.udp/serv_func.c: -------------------------------------------------------------------------------- 1 | /* 2 | * serv_func.c: functions for the server 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // Important variables used throughout 15 | #define MSGLEN 128 // size of datagrams 16 | #define SERVER_PORTNUM 2020 17 | #define HOSTLEN 512 18 | #define TICKET_AVAIL 0 19 | #define MAXUSERS 3 20 | #define oops(p) {perror(p); exit(1);} 21 | 22 | int sd = -1; // communications socket 23 | int ticket_array[MAXUSERS]; 24 | int num_tickets_out = 0; 25 | 26 | char * do_hello(); 27 | char * do_goodbye(); 28 | 29 | // setup: initialize server 30 | setup() 31 | { 32 | sd = make_dgram_server_socket(SERVER_PORTNUM); 33 | if (sd == -1) 34 | oops("make socket"); 35 | free_all_tickets(); 36 | return sd; 37 | } 38 | 39 | free_all_tickets() 40 | { 41 | int i; 42 | 43 | for (i = 0; i < MAXUSERS; i++) 44 | ticket_array[i] = TICKET_AVAIL; 45 | } 46 | 47 | shut_down() 48 | { 49 | close(sd); 50 | } 51 | 52 | // handle_request: branch on code in request 53 | handle_request(char * req, struct sockaddr_in * client, socklen_t addlen) 54 | { 55 | char * response; 56 | int ret; 57 | 58 | if (strncmp(req, "HELO", 4) == 0) 59 | response = do_hello(req); 60 | else if (strncmp(req, "GBYE", 4) == 0) 61 | response = do_goodbye(req); 62 | else 63 | response = "FAIL invalid request"; 64 | 65 | narrate("SAID:", response, client); 66 | ret = sendto(sd, response, strlen(response), 0, client, addlen); 67 | if (ret == -1) 68 | perror("SERVER sendto failed"); 69 | } 70 | 71 | // do_hello: give out a ticket if any are available 72 | char * do_hello(char * msg_p) 73 | { 74 | int x; 75 | static char replybuf[MSGLEN]; 76 | 77 | if (num_tickets_out >= MAXUSERS) 78 | return("FAIL no tickets available"); 79 | 80 | for (x = 0; x < MAXUSERS && ticket_array[x] != TICKET_AVAIL; x++) 81 | ; 82 | 83 | if (x == MAXUSERS) 84 | { 85 | narrate("database corrupt", "", NULL); 86 | return("FAIL database corrupt"); 87 | } 88 | 89 | // found a free ticket 90 | ticket_array[x] = atoi(msg_p + 5); 91 | sprintf(replybuf, "TICK %d.%d", ticket_array[x], x); 92 | num_tickets_out++; 93 | return(replybuf); 94 | } 95 | 96 | // do_goodbye: take back ticket from client 97 | char * do_goodbye(char * msg_p) 98 | { 99 | int pid, slot; 100 | 101 | // printf("msg_p+5 %s\n", msg_p+5); 102 | if ((sscanf((msg_p+5), "%d. %d", &pid, &slot) != 2) || 103 | (ticket_array[slot] != pid)) 104 | { 105 | narrate("Bogus ticket", msg_p+5, NULL); 106 | return("FAIL ivalid ticket"); 107 | } 108 | 109 | // the ticket is valid, release it 110 | ticket_array[slot] = TICKET_AVAIL; 111 | num_tickets_out--; 112 | 113 | return("THNX see ya"); 114 | } 115 | 116 | narrate(char * msg1, char * msg2, struct sockaddr_in * clientp) 117 | { 118 | fprintf(stderr, "\t\tSERVER: %s %s ", msg1, msg2); 119 | if (clientp) 120 | fprintf(stderr, "(%s:%d)", inet_ntoa(clientp->sin_addr), 121 | ntohs(clientp->sin_port)); 122 | putc('\n', stderr); 123 | } 124 | 125 | -------------------------------------------------------------------------------- /14.thread/test_cond: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/14.thread/test_cond -------------------------------------------------------------------------------- /14.thread/test_cond.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test_mutex.c 3 | * 4 | * pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) 5 | * 使线程挂起直到另一个线程通过条件变量发出消息.先自动释放指定的锁, 6 | * 然后等待条件变量的变化 7 | * pthread_cond_signal(pthread_cond_t * cond) 8 | * 通过条件变量cond 发消息 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | int times = 100000000; 15 | int total = 0; 16 | pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 17 | pthread_cond_t flag = PTHREAD_COND_INITIALIZER; 18 | 19 | struct arg_set 20 | { 21 | int count; 22 | int times; 23 | }; 24 | 25 | struct arg_set * mailbox; 26 | 27 | int main() 28 | { 29 | pthread_t t1, t2; 30 | 31 | void * add2(void *); 32 | 33 | int reports_in = 0; 34 | 35 | struct arg_set a1, a2; 36 | 37 | pthread_mutex_lock(&lock); 38 | 39 | a1.count = 0; 40 | a1.times = 10000000; 41 | a2.count = 0; 42 | a2.times = 1000; 43 | pthread_create(&t1, NULL, add2, (void *)&a1); 44 | pthread_create(&t2, NULL, add2, (void *)&a2); 45 | 46 | while (reports_in < 2) 47 | { 48 | printf("MAIN: waiting for flag\n"); 49 | pthread_cond_wait(&flag, &lock); 50 | printf("MAIN: I have the lock\n"); 51 | printf("%d\n", mailbox->count); 52 | total += mailbox->count; 53 | if (mailbox == &a1) 54 | pthread_join(t1, NULL); 55 | if (mailbox == &a2) 56 | pthread_join(t2, NULL); 57 | mailbox = NULL; 58 | pthread_cond_signal(&flag); 59 | reports_in ++; 60 | } 61 | 62 | printf("total %d\n", total); 63 | 64 | return 0; 65 | } 66 | 67 | void * add2(void * a) 68 | { 69 | struct arg_set *arg = a; 70 | for (int i = 0; i < arg->times; i++) 71 | { 72 | arg->count++; 73 | } 74 | 75 | printf("COUNT: waiting to get lock\n"); 76 | pthread_mutex_lock(&lock); 77 | printf("COUNT: have lock\n"); 78 | if (mailbox != NULL) 79 | pthread_cond_wait(&flag, &lock); 80 | mailbox = arg; 81 | pthread_cond_signal(&flag); 82 | pthread_mutex_unlock(&lock); 83 | 84 | return NULL; 85 | } 86 | -------------------------------------------------------------------------------- /14.thread/test_mutex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/14.thread/test_mutex -------------------------------------------------------------------------------- /14.thread/test_mutex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test_mutex.c 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | int total = 0; 9 | int times = 100; 10 | pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 11 | 12 | struct arg_set 13 | { 14 | int count; 15 | }; 16 | 17 | int main() 18 | { 19 | pthread_t t1, t2; 20 | 21 | /* 22 | void * add(); 23 | 24 | pthread_create(&t1, NULL, add, NULL); 25 | pthread_create(&t2, NULL, add, NULL); 26 | pthread_join(t1, NULL); 27 | pthread_join(t2, NULL); 28 | 29 | printf("%d\n", total); 30 | */ 31 | 32 | void * add2(void *); 33 | 34 | 35 | struct arg_set a1, a2; 36 | a1.count = 0; 37 | a2.count = 0; 38 | pthread_create(&t1, NULL, add2, (void *)&a1); 39 | pthread_create(&t2, NULL, add2, (void *)&a2); 40 | pthread_join(t1, NULL); 41 | pthread_join(t2, NULL); 42 | 43 | printf("%d\n", a1.count + a2.count); 44 | 45 | return 0; 46 | } 47 | 48 | // First method: add mutex 49 | void * add() 50 | { 51 | for (int i = 0; i < times; i++) 52 | { 53 | pthread_mutex_lock(&lock); 54 | total++; 55 | pthread_mutex_unlock(&lock); 56 | } 57 | } 58 | 59 | // Second method: cal single 60 | void * add2(void * a) 61 | { 62 | struct arg_set *arg = a; 63 | for (int i = 0; i < times; i++) 64 | { 65 | arg->count++; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /14.thread/test_thread: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/14.thread/test_thread -------------------------------------------------------------------------------- /14.thread/test_thread.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test_thread.c 3 | * 4 | * compile: cc test_thread.c -lpthread -o test_thread 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #define NUM 5 11 | 12 | main() 13 | { 14 | pthread_t t1, t2; // two threads 15 | 16 | void * print_msg(void *); 17 | 18 | pthread_create(&t1, NULL, print_msg, (void *)"hello "); 19 | pthread_create(&t2, NULL, print_msg, (void *)"world\n"); 20 | 21 | pthread_join(t1, NULL); 22 | pthread_join(t2, NULL); 23 | } 24 | 25 | void *print_msg(void * m) 26 | { 27 | char *cp = (char *)m; 28 | int i; 29 | for (i = 0; i < NUM; ++i) 30 | { 31 | printf("%s", m); 32 | // fflush(stdout); 33 | sleep(1); 34 | } 35 | 36 | return NULL; 37 | } 38 | -------------------------------------------------------------------------------- /15.IPC/file.txt: -------------------------------------------------------------------------------- 1 | Wed Sep 7 22:23:39 2016 2 | -------------------------------------------------------------------------------- /15.IPC/file_tc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/15.IPC/file_tc -------------------------------------------------------------------------------- /15.IPC/file_tc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * file_tc.c 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUFLEN 100 10 | 11 | main() 12 | { 13 | char file[20] = "file.txt"; 14 | int fd, nread; 15 | char buf[BUFLEN]; 16 | 17 | if ((fd = open(file, O_RDONLY)) == -1) 18 | printf("error"); 19 | 20 | 21 | lock_operation(fd, F_RDLCK); 22 | 23 | while ((nread = read(fd, buf, BUFLEN) > 0)) 24 | { 25 | printf("buf %s", buf); 26 | } 27 | 28 | 29 | lock_operation(fd, F_UNLCK); 30 | 31 | close(fd); 32 | } 33 | 34 | lock_operation(int fd, int op) 35 | { 36 | struct flock lock; 37 | 38 | lock.l_whence = SEEK_SET; 39 | lock.l_start = lock.l_len = 0; 40 | lock.l_pid = getpid(); 41 | lock.l_type = op; 42 | 43 | if (fcntl(fd, F_SETLKW, &lock) == -1) 44 | printf("error"); 45 | } 46 | -------------------------------------------------------------------------------- /15.IPC/file_ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/15.IPC/file_ts -------------------------------------------------------------------------------- /15.IPC/file_ts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * file_ts.c 3 | * 4 | * 文件锁 分两类: 5 | * 一个是写数据锁,告诉其他进程,在完成之前必须等待 6 | * 一个是读数据锁,告诉其他进程,要写文件必须等待,读文件不受影响 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | main() 15 | { 16 | char file[20] = "file.txt"; 17 | int fd; 18 | time_t now; 19 | char *message; 20 | 21 | if ((fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) 22 | printf("error"); 23 | 24 | while (1) 25 | { 26 | time(&now); 27 | message = ctime(&now); 28 | 29 | lock_operation(fd, F_WRLCK); 30 | 31 | if (lseek(fd, 0L, SEEK_SET) == -1) 32 | printf("error"); 33 | if (write(fd, message, strlen(message)) == -1) 34 | printf("error"); 35 | 36 | lock_operation(fd, F_UNLCK); 37 | sleep(1); 38 | } 39 | } 40 | 41 | lock_operation(int fd, int op) 42 | { 43 | struct flock lock; 44 | 45 | lock.l_whence = SEEK_SET; 46 | lock.l_start = lock.l_len = 0; 47 | lock.l_pid = getpid(); 48 | lock.l_type = op; 49 | 50 | if (fcntl(fd, F_SETLKW, &lock) == -1) 51 | printf("error"); 52 | } 53 | -------------------------------------------------------------------------------- /15.IPC/ipc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ipc.c 3 | */ 4 | 5 | /* 6 | * 三种方法让一个进程从另一个进程得到数据 7 | * 8 | * 1 文件. 通过读写文件的方式获取数据 9 | * 10 | * 2 命名管道. 是一个队列而不是常规文件,所以每次获取到数据都会清空管道 11 | * 12 | * 3 共享内存.同一个系统里的进程通过使用共享的内存段来交换数据 13 | * 14 | */ 15 | -------------------------------------------------------------------------------- /15.IPC/shm_tc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/15.IPC/shm_tc -------------------------------------------------------------------------------- /15.IPC/shm_tc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shm_tc.c 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define TIME_MEM_KEY 99 10 | #define SEG_SIZE ((size_t)100) 11 | 12 | main() 13 | { 14 | int seg_id; 15 | char * mem_ptr, *ctime(); 16 | long now; 17 | 18 | // create a shared memory segment 19 | seg_id = shmget(TIME_MEM_KEY, SEG_SIZE, 0777); 20 | if (seg_id == -1) 21 | printf("error"); 22 | 23 | // attach to it and get a pointer to where it attaches 24 | mem_ptr = shmat(seg_id, NULL, 0); 25 | if (mem_ptr == (void *)-1) 26 | printf("error"); 27 | 28 | printf("Time is %s", mem_ptr); 29 | 30 | shmdt(mem_ptr); 31 | } 32 | -------------------------------------------------------------------------------- /15.IPC/shm_ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tigeroses/unix_c/cfdaa4672efcca8b23732722d8e5875eb764c5d7/15.IPC/shm_ts -------------------------------------------------------------------------------- /15.IPC/shm_ts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shm_ts.c 3 | * 4 | * the time server using shared memory, a bizarre application 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define TIME_MEM_KEY 99 12 | #define SEG_SIZE ((size_t)100) 13 | 14 | main() 15 | { 16 | int seg_id; 17 | char * mem_ptr, *ctime(); 18 | long now; 19 | int n; 20 | 21 | // create a shared memory segment 22 | seg_id = shmget(TIME_MEM_KEY, SEG_SIZE, IPC_CREAT|0777); 23 | if (seg_id == -1) 24 | printf("error"); 25 | 26 | // attach to it and get a pointer to where it attaches 27 | mem_ptr = shmat(seg_id, NULL, 0); 28 | if (mem_ptr == (void *)-1) 29 | printf("error"); 30 | 31 | // run for a minute 32 | for (n = 0; n < 60; n++) 33 | { 34 | time(&now); 35 | strcpy(mem_ptr, ctime(&now)); 36 | sleep(1); 37 | } 38 | 39 | // remove it 40 | shmctl(seg_id, IPC_RMID, NULL); 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unix_c 2 | Linux环境C代码,来自书<Unix/Linux 编程实践教程> 3 | --------------------------------------------------------------------------------