Kernel: Add argc and argv to process entry
This commit is contained in:
		
							parent
							
								
									e0a7e242f8
								
							
						
					
					
						commit
						e0a72defa2
					
				|  | @ -25,13 +25,14 @@ continue_thread: | ||||||
| 	movq $0, %rax | 	movq $0, %rax | ||||||
| 	jmp *%rsi | 	jmp *%rsi | ||||||
| 
 | 
 | ||||||
| # void thread_jump_userspace(uint64_t rsp, uint64_t rip) | # void thread_userspace_trampoline(uint64_t rsp, uint64_t rip, int argc, char** argv) | ||||||
| .global thread_jump_userspace
 | .global thread_userspace_trampoline
 | ||||||
| thread_jump_userspace: | thread_userspace_trampoline: | ||||||
| 	pushq $0x23 | 	pushq $0x23 | ||||||
| 	pushq %rdi | 	pushq %rdi | ||||||
| 	pushfq | 	pushfq | ||||||
| 	pushq $0x1B | 	pushq $0x1B | ||||||
| 	pushq %rsi | 	pushq %rsi | ||||||
| 	movq $0, %rdi | 	movq %rdx, %rdi | ||||||
|  | 	movq %rcx, %rsi | ||||||
| 	iretq | 	iretq | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <BAN/NoCopyMove.h> | #include <BAN/NoCopyMove.h> | ||||||
| #include <BAN/RefPtr.h> | #include <BAN/RefPtr.h> | ||||||
|  | #include <kernel/Memory/Heap.h> | ||||||
| 
 | 
 | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| 
 | 
 | ||||||
|  | @ -27,11 +28,9 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 	public: | 	public: | ||||||
| 		static BAN::ErrorOr<Thread*> create(entry_t, void*, Process*); | 		static BAN::ErrorOr<Thread*> create(entry_t, void*, Process*); | ||||||
| 		static BAN::ErrorOr<Thread*> create_userspace(uintptr_t, Process*); | 		static BAN::ErrorOr<Thread*> create_userspace(uintptr_t, Process*, int, char**); | ||||||
| 		~Thread(); | 		~Thread(); | ||||||
| 
 | 
 | ||||||
| 		void jump_userspace(uintptr_t rip); |  | ||||||
| 
 |  | ||||||
| 		pid_t tid() const { return m_tid; } | 		pid_t tid() const { return m_tid; } | ||||||
| 
 | 
 | ||||||
| 		void set_rsp(uintptr_t rsp) { m_rsp = rsp; validate_stack(); } | 		void set_rsp(uintptr_t rsp) { m_rsp = rsp; validate_stack(); } | ||||||
|  | @ -44,7 +43,7 @@ namespace Kernel | ||||||
| 		void terminate() { m_state = State::Terminating; } | 		void terminate() { m_state = State::Terminating; } | ||||||
| 
 | 
 | ||||||
| 		uintptr_t stack_base() const { return (uintptr_t)m_stack_base; } | 		uintptr_t stack_base() const { return (uintptr_t)m_stack_base; } | ||||||
| 		size_t stack_size() const { return m_stack_size; } | 		size_t stack_size() const { return m_is_userspace ? m_userspace_stack_size : m_kernel_stack_size; } | ||||||
| 
 | 
 | ||||||
| 		uintptr_t interrupt_stack_base() const { return (uintptr_t)m_interrupt_stack; } | 		uintptr_t interrupt_stack_base() const { return (uintptr_t)m_interrupt_stack; } | ||||||
| 		uintptr_t interrupt_stack_size() const { return m_interrupt_stack_size; } | 		uintptr_t interrupt_stack_size() const { return m_interrupt_stack_size; } | ||||||
|  | @ -57,23 +56,33 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		Thread(pid_t tid, Process*); | 		Thread(pid_t tid, Process*); | ||||||
|  | 		void on_exit(); | ||||||
| 		 | 		 | ||||||
| 		void validate_stack() const; | 		void validate_stack() const; | ||||||
| 
 | 
 | ||||||
| 		BAN::ErrorOr<void> initialize(entry_t, void*); | 	private: | ||||||
| 		void on_exit(); | 		struct userspace_entry_t | ||||||
|  | 		{ | ||||||
|  | 			uintptr_t entry; | ||||||
|  | 			int argc  { 0 }; | ||||||
|  | 			char** argv  { 0 }; | ||||||
|  | 		}; | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		static constexpr size_t m_stack_size			= 4096 * 1; | 		static constexpr size_t m_kernel_stack_size		= PAGE_SIZE * 1; | ||||||
| 		static constexpr size_t m_interrupt_stack_size	= 4096; | 		static constexpr size_t m_userspace_stack_size	= PAGE_SIZE * 1; | ||||||
| 		void*		m_interrupt_stack	{ nullptr }; | 		static constexpr size_t m_interrupt_stack_size	= PAGE_SIZE; | ||||||
| 		void*		m_stack_base		{ nullptr }; | 		vaddr_t		m_interrupt_stack	{ 0 }; | ||||||
|  | 		vaddr_t		m_stack_base		{ 0 }; | ||||||
| 		uintptr_t	m_rip				{ 0 }; | 		uintptr_t	m_rip				{ 0 }; | ||||||
| 		uintptr_t	m_rsp				{ 0 }; | 		uintptr_t	m_rsp				{ 0 }; | ||||||
| 		const pid_t	m_tid				{ 0 }; | 		const pid_t	m_tid				{ 0 }; | ||||||
| 		State		m_state				{ State::NotStarted }; | 		State		m_state				{ State::NotStarted }; | ||||||
| 		Process*	m_process			{ nullptr }; | 		Process*	m_process			{ nullptr }; | ||||||
| 		bool		m_in_syscall		{ false }; | 		bool		m_in_syscall		{ false }; | ||||||
|  | 		bool		m_is_userspace		{ false }; | ||||||
|  | 
 | ||||||
|  | 		userspace_entry_t m_userspace_entry; | ||||||
| 
 | 
 | ||||||
| 		friend class Scheduler; | 		friend class Scheduler; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include <kernel/CriticalScope.h> | #include <kernel/CriticalScope.h> | ||||||
| #include <kernel/Memory/FixedWidthAllocator.h> | #include <kernel/Memory/FixedWidthAllocator.h> | ||||||
| #include <kernel/Memory/MMU.h> | #include <kernel/Memory/MMU.h> | ||||||
|  | #include <kernel/Memory/MMUScope.h> | ||||||
| #include <kernel/Process.h> | #include <kernel/Process.h> | ||||||
| 
 | 
 | ||||||
| namespace Kernel | namespace Kernel | ||||||
|  | @ -20,9 +21,7 @@ namespace Kernel | ||||||
| 		m_allocated_pages = m_mmu.get_free_page(); | 		m_allocated_pages = m_mmu.get_free_page(); | ||||||
| 		m_mmu.map_page_at(allocated_pages_paddr, m_allocated_pages, MMU::Flags::ReadWrite | MMU::Flags::Present); | 		m_mmu.map_page_at(allocated_pages_paddr, m_allocated_pages, MMU::Flags::ReadWrite | MMU::Flags::Present); | ||||||
| 
 | 
 | ||||||
| 		CriticalScope _; | 		MMUScope _(m_mmu); | ||||||
| 
 |  | ||||||
| 		m_mmu.load(); |  | ||||||
| 
 | 
 | ||||||
| 		memset((void*)m_nodes_page, 0, PAGE_SIZE); | 		memset((void*)m_nodes_page, 0, PAGE_SIZE); | ||||||
| 		memset((void*)m_allocated_pages, 0, PAGE_SIZE); | 		memset((void*)m_allocated_pages, 0, PAGE_SIZE); | ||||||
|  | @ -38,8 +37,6 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 		m_free_list = node_table; | 		m_free_list = node_table; | ||||||
| 		m_used_list = nullptr; | 		m_used_list = nullptr; | ||||||
| 
 |  | ||||||
| 		Process::current().mmu().load(); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	FixedWidthAllocator::~FixedWidthAllocator() | 	FixedWidthAllocator::~FixedWidthAllocator() | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| #include <kernel/FS/VirtualFileSystem.h> | #include <kernel/FS/VirtualFileSystem.h> | ||||||
| #include <kernel/LockGuard.h> | #include <kernel/LockGuard.h> | ||||||
| #include <kernel/Memory/Heap.h> | #include <kernel/Memory/Heap.h> | ||||||
|  | #include <kernel/Memory/MMUScope.h> | ||||||
| #include <kernel/Process.h> | #include <kernel/Process.h> | ||||||
| #include <kernel/Scheduler.h> | #include <kernel/Scheduler.h> | ||||||
| #include <LibELF/ELF.h> | #include <LibELF/ELF.h> | ||||||
|  | @ -87,11 +88,9 @@ namespace Kernel | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				{ | 				{ | ||||||
| 					CriticalScope _; | 					MMUScope _(process->mmu()); | ||||||
| 					process->mmu().load(); |  | ||||||
| 					memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz); | 					memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz); | ||||||
| 					memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz); | 					memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz); | ||||||
| 					Process::current().mmu().load(); |  | ||||||
| 				} | 				} | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
|  | @ -100,7 +99,16 @@ namespace Kernel | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		auto* thread = MUST(Thread::create_userspace(elf_file_header.e_entry, process)); | 		char** argv = nullptr; | ||||||
|  | 		{ | ||||||
|  | 			MMUScope _(process->mmu()); | ||||||
|  | 			argv = (char**)MUST(process->allocate(sizeof(char**) * 1)); | ||||||
|  | 			argv[0] = (char*)MUST(process->allocate(path.size() + 1)); | ||||||
|  | 			memcpy(argv[0], path.data(), path.size()); | ||||||
|  | 			argv[0][path.size()] = '\0'; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		auto* thread = MUST(Thread::create_userspace(elf_file_header.e_entry, process, 1, argv)); | ||||||
| 		process->add_thread(thread); | 		process->add_thread(thread); | ||||||
| 
 | 
 | ||||||
| 		delete elf; | 		delete elf; | ||||||
|  |  | ||||||
|  | @ -1,16 +1,15 @@ | ||||||
| #include <BAN/Errors.h> | #include <BAN/Errors.h> | ||||||
|  | #include <kernel/CriticalScope.h> | ||||||
| #include <kernel/InterruptController.h> | #include <kernel/InterruptController.h> | ||||||
| #include <kernel/Memory/kmalloc.h> | #include <kernel/Memory/kmalloc.h> | ||||||
| #include <kernel/Process.h> | #include <kernel/Process.h> | ||||||
| #include <kernel/Scheduler.h> | #include <kernel/Scheduler.h> | ||||||
| #include <kernel/Thread.h> | #include <kernel/Thread.h> | ||||||
| 
 | 
 | ||||||
| #define PAGE_SIZE 4096 |  | ||||||
| 
 |  | ||||||
| namespace Kernel | namespace Kernel | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| 	extern "C" void thread_jump_userspace(uintptr_t rsp, uintptr_t rip); | 	extern "C" void thread_userspace_trampoline(uint64_t rsp, uint64_t rip, int argc, char** argv); | ||||||
| 
 | 
 | ||||||
| 	template<size_t size, typename T> | 	template<size_t size, typename T> | ||||||
| 	static void write_to_stack(uintptr_t& rsp, const T& value) | 	static void write_to_stack(uintptr_t& rsp, const T& value) | ||||||
|  | @ -19,32 +18,66 @@ namespace Kernel | ||||||
| 		memcpy((void*)rsp, (void*)&value, size); | 		memcpy((void*)rsp, (void*)&value, size); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	static pid_t s_next_tid = 1; | ||||||
|  | 
 | ||||||
| 	BAN::ErrorOr<Thread*> Thread::create(entry_t entry, void* data, Process* process) | 	BAN::ErrorOr<Thread*> Thread::create(entry_t entry, void* data, Process* process) | ||||||
| 	{ | 	{ | ||||||
| 		static pid_t next_tid = 1; | 		// Create the thread object
 | ||||||
| 		auto* thread = new Thread(next_tid++, process); | 		Thread* thread = new Thread(s_next_tid++, process); | ||||||
| 		if (thread == nullptr) | 		if (thread == nullptr) | ||||||
| 			return BAN::Error::from_errno(ENOMEM); | 			return BAN::Error::from_errno(ENOMEM); | ||||||
| 		TRY(thread->initialize(entry, data)); | 
 | ||||||
|  | 		// Initialize stack and registers
 | ||||||
|  | 		thread->m_stack_base = (vaddr_t)kmalloc(m_kernel_stack_size, PAGE_SIZE); | ||||||
|  | 		if (thread->m_stack_base == 0) | ||||||
|  | 			return BAN::Error::from_errno(ENOMEM); | ||||||
|  | 		thread->m_rsp = (uintptr_t)thread->m_stack_base + m_kernel_stack_size; | ||||||
|  | 		thread->m_rip = (uintptr_t)entry;		 | ||||||
|  | 
 | ||||||
|  | 		// Initialize stack for returning
 | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, thread); | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, &Thread::on_exit); | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, data); | ||||||
|  | 
 | ||||||
| 		return thread; | 		return thread; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<Thread*> Thread::create_userspace(uintptr_t entry, Process* process) | 	BAN::ErrorOr<Thread*> Thread::create_userspace(uintptr_t entry, Process* process, int argc, char** argv) | ||||||
| 	{ | 	{ | ||||||
| 		Thread* thread = TRY(Thread::create( | 		// Create the thread object
 | ||||||
| 			[](void* entry) | 		Thread* thread = new Thread(s_next_tid++, process); | ||||||
| 			{ | 		if (thread == nullptr) | ||||||
| 				Thread::current().jump_userspace((uintptr_t)entry); |  | ||||||
| 				ASSERT_NOT_REACHED(); |  | ||||||
| 			}, (void*)entry, process |  | ||||||
| 		)); |  | ||||||
| 		thread->m_interrupt_stack = kmalloc(m_interrupt_stack_size, PAGE_SIZE); |  | ||||||
| 		if (thread->m_interrupt_stack == nullptr) |  | ||||||
| 		{ |  | ||||||
| 			delete thread; |  | ||||||
| 			return BAN::Error::from_errno(ENOMEM); | 			return BAN::Error::from_errno(ENOMEM); | ||||||
|  | 		thread->m_is_userspace = true; | ||||||
|  | 
 | ||||||
|  | 		// Allocate stack
 | ||||||
|  | 		thread->m_stack_base = (uintptr_t)kmalloc(m_userspace_stack_size, PAGE_SIZE); | ||||||
|  | 		ASSERT(thread->m_stack_base); | ||||||
|  | 		process->mmu().identity_map_range(thread->m_stack_base, m_userspace_stack_size, MMU::Flags::UserSupervisor | MMU::Flags::ReadWrite | MMU::Flags::Present); | ||||||
|  | 
 | ||||||
|  | 		// Allocate interrupt stack
 | ||||||
|  | 		thread->m_interrupt_stack = (vaddr_t)kmalloc(m_interrupt_stack_size, PAGE_SIZE); | ||||||
|  | 		ASSERT(thread->m_interrupt_stack); | ||||||
|  | 
 | ||||||
|  | 		thread->m_userspace_entry = { .entry = entry, .argc = argc, .argv = argv }; | ||||||
|  | 
 | ||||||
|  | 		// Setup registers and entry
 | ||||||
|  | 		static entry_t entry_trampoline( | ||||||
|  | 			[](void*) | ||||||
|  | 			{ | ||||||
|  | 				userspace_entry_t& entry = Thread::current().m_userspace_entry; | ||||||
|  | 				thread_userspace_trampoline(Thread::current().rsp(), entry.entry, entry.argc, entry.argv); | ||||||
|  | 				ASSERT_NOT_REACHED(); | ||||||
| 			} | 			} | ||||||
| 		process->mmu().identity_map_range(thread->stack_base(), thread->stack_size(), MMU::Flags::UserSupervisor | MMU::Flags::ReadWrite | MMU::Flags::Present); | 		); | ||||||
|  | 		thread->m_rsp = thread->m_stack_base + m_userspace_stack_size; | ||||||
|  | 		thread->m_rip = (uintptr_t)entry_trampoline; | ||||||
|  | 
 | ||||||
|  | 		// Setup stack for returning
 | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, thread); | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, &Thread::on_exit); | ||||||
|  | 		write_to_stack<sizeof(void*)>(thread->m_rsp, nullptr); | ||||||
|  | 
 | ||||||
| 		return thread; | 		return thread; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -63,32 +96,13 @@ namespace Kernel | ||||||
| 		return *m_process; | 		return *m_process; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<void> Thread::initialize(entry_t entry, void* data) |  | ||||||
| 	{ |  | ||||||
| 		m_stack_base = kmalloc(m_stack_size, PAGE_SIZE); |  | ||||||
| 		if (m_stack_base == nullptr) |  | ||||||
| 			return BAN::Error::from_errno(ENOMEM); |  | ||||||
| 		m_rsp = (uintptr_t)m_stack_base + m_stack_size; |  | ||||||
| 		m_rip = (uintptr_t)entry; |  | ||||||
| 
 |  | ||||||
| 		write_to_stack<sizeof(void*)>(m_rsp, this); |  | ||||||
| 		write_to_stack<sizeof(void*)>(m_rsp, &Thread::on_exit); |  | ||||||
| 		write_to_stack<sizeof(void*)>(m_rsp, data); |  | ||||||
| 
 |  | ||||||
| 		return {}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	Thread::~Thread() | 	Thread::~Thread() | ||||||
| 	{ | 	{ | ||||||
| 		dprintln("thread {} ({}) exit", tid(), m_process->pid()); | 		dprintln("thread {} ({}) exit", tid(), m_process->pid()); | ||||||
| 		if (m_interrupt_stack) |  | ||||||
| 			kfree(m_interrupt_stack); |  | ||||||
| 		kfree(m_stack_base); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	void Thread::jump_userspace(uintptr_t rip) | 		if (m_interrupt_stack) | ||||||
| 	{ | 			kfree((void*)m_interrupt_stack); | ||||||
| 		thread_jump_userspace(rsp(), rip); | 		kfree((void*)m_stack_base); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void Thread::validate_stack() const | 	void Thread::validate_stack() const | ||||||
|  |  | ||||||
|  | @ -180,7 +180,7 @@ static void init2(void* tty1) | ||||||
| 
 | 
 | ||||||
| 	((TTY*)tty1)->initialize_device(); | 	((TTY*)tty1)->initialize_device(); | ||||||
| 
 | 
 | ||||||
| 	MUST(Process::create_userspace("/usr/bin/test"sv)); | 	MUST(Process::create_userspace("/usr/bin/cat"sv)); | ||||||
| 	return; | 	return; | ||||||
| 
 | 
 | ||||||
| 	Process::create_kernel( | 	Process::create_kernel( | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.26) | ||||||
| project(userspace CXX) | project(userspace CXX) | ||||||
| 
 | 
 | ||||||
| set(USERSPACE_PROJECTS | set(USERSPACE_PROJECTS | ||||||
|  | 	cat | ||||||
| 	test | 	test | ||||||
| 	yes | 	yes | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | cmake_minimum_required(VERSION 3.26) | ||||||
|  | 
 | ||||||
|  | project(cat CXX) | ||||||
|  | 
 | ||||||
|  | set(SOURCES | ||||||
|  | 	main.cpp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | add_executable(cat ${SOURCES}) | ||||||
|  | target_compile_options(cat PUBLIC -O2 -g) | ||||||
|  | add_dependencies(cat libc-install) | ||||||
|  | target_link_options(cat PUBLIC -nodefaultlibs -lc) | ||||||
|  | 
 | ||||||
|  | add_custom_target(cat-install | ||||||
|  | 	COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cat ${BANAN_BIN}/ | ||||||
|  | 	DEPENDS cat | ||||||
|  | ) | ||||||
|  | @ -0,0 +1,50 @@ | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | bool cat_file(FILE* fp) | ||||||
|  | { | ||||||
|  | 	char buffer[1024]; | ||||||
|  | 	size_t n_read; | ||||||
|  | 	while ((n_read = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) | ||||||
|  | 	{ | ||||||
|  | 		buffer[n_read] = '\0'; | ||||||
|  | 		fputs(buffer, stdout); | ||||||
|  | 	} | ||||||
|  | 	if (ferror(fp)) | ||||||
|  | 	{ | ||||||
|  | 		perror("fread"); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char** argv) | ||||||
|  | { | ||||||
|  | 	int ret = 0; | ||||||
|  | 
 | ||||||
|  | 	printf("argc %d, argv %p\n", argc, argv); | ||||||
|  | 	for (int i = 0; i < argc; i++) | ||||||
|  | 		printf("%s\n", argv[i]); | ||||||
|  | 
 | ||||||
|  | 	if (argc > 1) | ||||||
|  | 	{ | ||||||
|  | 		for (int i = 1; i < argc; i++) | ||||||
|  | 		{ | ||||||
|  | 			FILE* fp = fopen(argv[i], "r"); | ||||||
|  | 			if (fp == nullptr) | ||||||
|  | 			{ | ||||||
|  | 				perror(argv[i]); | ||||||
|  | 				ret = 1; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (cat_file(fp)) | ||||||
|  | 				ret = 1; | ||||||
|  | 			fclose(fp); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		ret = cat_file(stdin); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | @ -1,36 +1,12 @@ | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <math.h> |  | ||||||
| 
 | 
 | ||||||
| #define ERROR(msg) { perror(msg); return 1; } | #define ERROR(msg) { perror(msg); return 1; } | ||||||
| #define BUF_SIZE 1024 | #define BUF_SIZE 1024 | ||||||
| 
 | 
 | ||||||
| int main() | int main() | ||||||
| { | { | ||||||
| 	printf("%.2e\n", 1230.0); |  | ||||||
| 	printf("%.2e\n", 123.0); |  | ||||||
| 	printf("%.2e\n", 12.3); |  | ||||||
| 	printf("%.2e\n", 1.23); |  | ||||||
| 	printf("%.2e\n", 0.123); |  | ||||||
| 	printf("%.2e\n", 0.0123); |  | ||||||
| 	printf("%.2e\n", 0.00123); |  | ||||||
| 
 |  | ||||||
| 	printf("%e\n", 123.456); |  | ||||||
| 	printf("%.2e\n", 123.456); |  | ||||||
| 	printf("%.0e\n", 123.456); |  | ||||||
| 	printf("%#.0e\n", 123.456); |  | ||||||
| 
 |  | ||||||
| 	printf("%e\n", -123.456); |  | ||||||
| 	printf("%.2e\n", -123.456); |  | ||||||
| 	printf("%.0e\n", -123.456); |  | ||||||
| 	printf("%#.0e\n", -123.456); |  | ||||||
| 
 |  | ||||||
| 	printf("%e\n", 0.0); |  | ||||||
| 	printf("%e\n", -0.0); |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| 
 |  | ||||||
| 	FILE* fp = fopen("/usr/include/stdio.h", "r"); | 	FILE* fp = fopen("/usr/include/stdio.h", "r"); | ||||||
| 	if (fp == NULL) | 	if (fp == NULL) | ||||||
| 		ERROR("fopen"); | 		ERROR("fopen"); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue