commit a9a0ade08c161682574d4842fdd0c1bb91c2016b Author: Thomas "Cakeisalie5" Touhey Date: Wed Dec 21 22:56:20 2016 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..377570b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/Makefile.cfg +/obj +/man +/p7 +/p7screen diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..20e505d --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,2 @@ +# p7utils authors +Thomas "Cakeisalie5" is the man behind these utilities. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0daa041 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,336 @@ +GNU General Public License +========================== + +_Version 2, June 1991_ +_Copyright © 1989, 1991 Free Software Foundation, Inc.,_ +_51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA_ + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +### Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: **(1)** copyright the software, and +**(2)** offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The “Program”, below, +refers to any such program or work, and a “work based on the Program” +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term “modification”.) Each licensee is addressed as “you”. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +**1.** You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +**2.** You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +* **a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. +* **b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. +* **c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +* **a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, +* **b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, +* **c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +**6.** Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +**7.** If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**8.** If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**9.** The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and “any +later version”, you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +**10.** If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +### NO WARRANTY + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w` and `show c`; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a “copyright disclaimer” for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..a9e0685 --- /dev/null +++ b/Makefile @@ -0,0 +1,179 @@ +#!/usr/bin/make -f +#******************************************************************************# +# Include variables and message subsystem # +#******************************************************************************# +include Makefile.vars Makefile.msg + +#******************************************************************************# +# General targets # +#******************************************************************************# +# Make it all +all: all-bins all-doc + +# Mostly clean +mostlyclean: mostlyclean-bins + mclean: mostlyclean + +# Clean it all +clean: clean-bins clean-doc + fclean: clean + +# Clean it entirely +mrproper: clean + $(call rmsg,Removing configuration.) + $(call rcmd,Makefile.cfg,$(RM) Makefile.cfg) + +# Remake it all +re: clean all + +# Install it all +install: install-bins install-doc + +# Uninstall it all +uninstall: uninstall-bins uninstall-doc + +# Reinstall it all +reinstall: uninstall install + +.PHONY: all mostlyclean mclean clean fclean mrproper re +.PHONY: install uninstall reinstall +#******************************************************************************# +# Configuration (version) checking dependencies # +#******************************************************************************# +# Define the dependencies. + CHECKCFG := $(if $(shell test -f Makefile.cfg || echo y),check-config, \ + $(if $(shell [ "$(VERSION)" = "$(CONFIG_VERSION)" ] || echo y), \ + check-config-version)) + +# Define the rules. + check-config: + @printf "\033[1;31mNo configuration file found!\n" + @printf "You should configure before re-running this target.\033[0m\n" + @false + check-config-version: + @printf "\033[1;31mConfiguration version is incorrect!\n" + @printf "You should re-configure before re-running this target.\033[0m\n" + @false + +.PHONY: check-config check-config-version +#******************************************************************************# +# Information getting from the Makefile variables # +#******************************************************************************# +# Get the project name. + getname: + @echo "$(NAME)" + +# Get the project version. + getversion: + @echo "$(VERSION)" + +# Get the project author. + getauthor: + @echo "$(AUTHOR_NAME) <$(AUTHOR_MAIL)>" + getauthor_name: + @echo "$(AUTHOR_NAME)" + +.PHONY: getname getauthor getauthor_name getversion +#******************************************************************************# +# Binaries-specific targets # +#******************************************************************************# +# Make the binaries. + all-bins: $(CHECKCFG) $(BINARIES:%=%$(if $(FOR_WINDOWS),.exe)) + +# Make a binary object directory. + $(BINARIES:%=$(OBJDIR)/%): + $(call bcmd,mkdir,$@,$(MD) $@) + +# Make an object out of a C source file. +define make-binaryobj-rule + $(OBJDIR)/$1/%.o: $(SRCDIR)/$1/%.c | $(OBJDIR)/$1 + $(call bcmd,cc,$$@,$(CC) -c -o $$@ $$< $(CFLAGS)) +endef +$(foreach bin,$(BINARIES),\ +$(eval $(call make-binaryobj-rule,$(bin)))) + +# Make a binary +define make-binary-rule + $1$(if $(FOR_WINDOWS),.exe): $(SRC_$1:%=$(OBJDIR)/$1/%.o) | $(OBJDIR)/$1 + $(call bcmd,ld,$$@,$(LD) -o $$@ $$^ $(LDFLAGS)) +endef +$(foreach bin,$(BINARIES),\ +$(eval $(call make-binary-rule,$(bin)))) + +# Remove object files. + mostlyclean-bins: + $(call rmsg,Removing objects directory.) + $(call qcmd,$(RM) -r $(OBJDIR)) + mclean-bins: mostlyclean-bins + +# Clean and remove binaries. + clean-bins: mostlyclean-bins + $(call rmsg,Removing binaries.) + $(call qcmd,$(RM) $(BINARIES:%=%*)) + +# Install binaries + install-bins: all-bins + $(call imsg,Installing binaries.) + $(call qcmd,$(INSTALL) -m 755 -d "$(IBINDIR)") + $(call qcmd,$(INSTALL) -m 755 -t "$(IBINDIR)" \ + $(BINARIES:%=%$(if $(FOR_WINDOWS),.exe))) + +# Uninstall binaries + uninstall-bins: $(CHECKCFG) + $(call rmsg,Uninstalling binaries.) + $(call qcmd,$(RM) $(BINARIES) $(BINARIES:%=%.exe)) + +.PHONY: all-bins mostlyclean-bins mclean-bins clean-bins re-bins +.PHONY: install-bins uninstall-bins +#******************************************************************************# +# Documentation-related # +#******************************************************************************# +# Make all manpages + all-doc: $(foreach s,$(MAN_SECTIONS), $(MAN_$(s):%=$(MANDIR)/man$(s)/%.$(s))) + +# Make manpages directories + $(MAN_SECTIONS:%=$(MANDIR)/man%): + $(call bcmd,mkdir,$@,$(MD) $@) + +# Make-A-Manpage +define make-manpage-rule + $(MANDIR)/man$1/%.$1: $(DOCDIR)/%.$1.txt | $(MANDIR)/man$1 + $(call bcmd,a2x,$$<,$(A2X) -f manpage -D $$| $$< 2>/dev/null) +endef +$(foreach section, $(MAN_SECTIONS), \ +$(eval $(call make-manpage-rule,$(section)))) + +# Remove all manpages + clean-doc: + $(call rmsg,Removing manpages directory.) + $(call rcmd,$(MANDIR),$(RM) -r $(MANDIR)) + +# Remake all manpages + re-doc: clean-doc all-doc + +# Install a manpage section. +define make-installmansection-rule + install-doc-$1: $(MAN_$1:%=$(MANDIR)/man$1/%.$1) + $(call imsg,Installing manpages section $1.) + $(call qcmd,$(INST) -m 755 -d "$(IMANDIR)/man$1") + $(foreach i,$(MAN_$1),$(call icmd,man$1/$(i).$1)) + $(call qcmd,$(INST) -m 644 -t "$(IMANDIR)/man$1" \ + $(MAN_$1:%=$(MANDIR)/man$1/%.$1)) + $(call qcmd,$(GZIP) $(MAN_$1:%="$(IMANDIR)/man$1/%.$1")) +endef +$(foreach section, $(MAN_SECTIONS), \ +$(eval $(call make-installmansection-rule,$(section)))) + +# Install manpages + install-doc: $(CHECKCFG) $(if $(INSTALL_MANPAGES),\ + $(MAN_SECTIONS:%=install-doc-%)) + +# Uninstall manpages + uninstall-doc: $(CHECKCFG) + $(call rmsg,Removing manpages.) + $(call qcmd,$(RM) $(foreach s,$(MAN_SECTIONS),\ + $(patsubst %,"$(IMANDIR)"/man$(s)/%.$(s)*,$(MAN_$(s))))) + +.PHONY: all-doc clean-doc re-doc install-doc uninstall-doc +.PHONY: $(MAN_SECTIONS:%=install-doc-%) +# End of file. diff --git a/Makefile.msg b/Makefile.msg new file mode 100755 index 0000000..186d90a --- /dev/null +++ b/Makefile.msg @@ -0,0 +1,58 @@ +#!/usr/bin/make -f +# The Makefile message subsystem. +# For nice logs. 5 dollars per log only. +#******************************************************************************# +# Colors and misc # +#******************************************************************************# +# Used colors ANSI modifiers escape codes + color_green := 32 + color_red := 31 + color_yellow := 33 + +# Newline - comes handy in some situations +define \n + + +endef + +#******************************************************************************# +# General messages # +#******************************************************************************# +# Command message - display basic info about the command, and run it. +define cmd +@$(if $(MAKE_FULL_LOG),,printf "\033[1;""$4""m>\033[0m \033[1m%s\033[0m %s\n" "$1" "$2";) + $(if $(MAKE_FULL_LOG),,@)$3 +endef + +# Quiet command - make it non-quiet if full log is enabled. +define qcmd +$(if $(MAKE_FULL_LOG),,@)$1 +endef + +# Normal message - display it. +define msg +$(if $(MAKE_FULL_LOG),,@printf "\033[1;""$2""m>\033[0m \033[1m%s\033[0m\n" "$1") +endef + +#******************************************************************************# +# Commands # +#******************************************************************************# +# Build command +define bcmd +$(call cmd,$1,$2,$3,$(color_green)) +endef + +#******************************************************************************# +# Messages # +#******************************************************************************# +# Remove message +define rmsg +$(call msg,$1,$(color_red)) +endef + +# Install message +define imsg +$(call msg,$1,$(color_yellow)) +endef + +# End of file diff --git a/Makefile.vars b/Makefile.vars new file mode 100755 index 0000000..207c9ee --- /dev/null +++ b/Makefile.vars @@ -0,0 +1,126 @@ +#!/usr/bin/make -f +#******************************************************************************# +# Include configuration # +#******************************************************************************# +-include Makefile.cfg + +# Correct target and adapt + TARGET := $(if $(TARGET),$(TARGET)-) + FOR_WINDOWS := $(if $(findstring mingw,$(TARGET)),y) + +#******************************************************************************# +# Project main information # +#******************************************************************************# +# Project name. + NAME := p7utils + +# Required packages + LIBS := libp7 sdl + +# Author information. + AUTHOR_NAME := Thomas \"Cakeisalie5\" Touhey + AUTHOR_MAIL := thomas@touhey.fr + +# Project license. + LICENSE := GPLv2 + +# Project version. + MAJOR := 2 + MINOR := 0 + INDEV := yes + +# Project version string. + VERSION := $(MAJOR).$(MINOR)$(if $(INDEV),-indev) + +#******************************************************************************# +# Project directories # +#******************************************************************************# +# Sources directory + SRCDIR := ./src + +# Objects directory + OBJDIR := ./obj + +# Manpages sources directory + DOCDIR := ./doc + +# Manpages directory + MANDIR := ./man + +#******************************************************************************# +# Binary utilities # +#******************************************************************************# +# Package configuration + PKGCONFIG := $(TARGET)pkg-config + +# C Compiler + CC := $(TARGET)gcc +# - Check flags (enable warnings) + CWARN := -Wall -Wextra +# - More flags (profiling, ...) +#CMOREFLAGS := +# - All C Compiler flags + CFLAGS := $(CWARN) -std=gnu11 -fPIC -O2 \ + -D LICENSE="$(LICENSE)" -D VERSION="$(VERSION)" \ + -D AUTHOR="$(AUTHOR_NAME) <$(AUTHOR_MAIL)>" \ + -D DEFAULT_STORAGE="$(DEFAULT_STORAGE)" \ + -D DEFAULT_ZOOM="$(DEFAULT_ZOOM)" \ + $(shell $(PKGCONFIG) --cflags $(LIBS) 2>/dev/null) \ + $(CMOREFLAGS) + +# Linker + LD := $(TARGET)gcc +# - Specific linker flags + LDFLAGS_Linux := -Wl,-z,relro +# - Linker flags + LDFLAGS := $(shell $(PKGCONFIG) --libs $(LIBS) 2>/dev/null) \ + $(if $(FOR_WINDOWS),,$(LDFLAGS_Linux)) + +# Directory maker + MD := mkdir -p +# File remover + RM := rm -f +# Installer + INSTALL := install +# Asciidoc + A2X := a2x + +#******************************************************************************# +# Binaries and sources # +#******************************************************************************# +# Look for binaries + BINARIES := $(notdir $(shell find $(SRCDIR) -mindepth 1 -maxdepth 1 \ + -type d | sort)) + +# Look for their sources +define get-binary-sources + SRC_$1 := $(basename $(shell find $(SRCDIR)/$1 \ + -maxdepth 1 -mindepth 1 -type f -name "*.c" -printf "%P\n" | sort)) +endef +$(foreach bin,$(BINARIES), \ +$(eval $(call get-binary-sources,$(bin)))) + +#******************************************************************************# +# Look for manpages # +#******************************************************************************# +# Get the manpages sections and contents + MAN_SECTIONS := +define check-man + MAN_SECTIONS += $1 + MAN_$1 += $2 +endef +$(foreach doc, $(basename $(shell find $(DOCDIR) \ + -maxdepth 1 -mindepth 1 -printf "%P\n" -type f -or -type l -name "*.*.txt")), \ +$(eval $(call check-man,$(patsubst .%,%,$(suffix $(doc))),$(basename $(doc))))) + +# Remove duplicate sections. + MAN_SECTIONS := $(sort $(MAN_SECTIONS)) + +#******************************************************************************# +# Check for DESTDIR (add as prefix to installation root) # +#******************************************************************************# +define add-dest-dir + $1 = $(DESTDIR)$($1) +endef +$(if $(DESTDIR), $(foreach idir,IBINDIR IMANDIR, \ +$(eval $(call add-dest-dir,$(idir))))) diff --git a/README.md b/README.md new file mode 100644 index 0000000..560665b --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# p7utils - Shell utilities for libp7 +## Introduction +**p7utils** is a set of command-line utilities that allow you to interact with +your calculator. `p7` will interact with the storage memories, `p7screen` will +display the result of the screen streaming and `p7os` will play with the +calculator's OS (backup, flash). + +## Prerequisites +### Making-only dependencies +| Name | Version | +| -------------------------------------------------- | -------- | +| [gcc](https://gcc.gnu.org/) | >= 4.9 | +| [binutils](https://www.gnu.org/software/binutils/) | >= 2.25 | +| [asciidoc](http://asciidoc.org/) | >= 8.6.9 | +| [gzip](https://www.gnu.org/software/gzip/) | >= 1.6 | + +### Making and runtime dependencies +| Name | Version | +| -------------------------------------------------- | --------- | +| [libp7](https://forge.touhey.fr/lib/p7/) | >= 1.5 | +| [libsdl](https://libsdl.org/) | == 1.2.15 | + +## Building +Just `./configure` then `make`. To install, use `make install`. +Make the manpages with `make all-doc` and install them with `make install-doc`. + +Other useful targets : +- `clean`, `fclean`, `clean-doc`: remove built files ; +- `re`, `re-doc`: regenerate binary and/or manpages. diff --git a/configure b/configure new file mode 100755 index 0000000..527f227 --- /dev/null +++ b/configure @@ -0,0 +1,170 @@ +#!/bin/sh +cd "$(dirname "$0")" +#******************************************************************************# +# Defaults # +#******************************************************************************# +# Project variables +name="$(make -s getname)" +version="$(make -s getversion)" + +# Author +author="$(make -s getauthor)" +author_name="$(make -s getauthor_name)" + +# Target +target="" + +# Make options +make_full_log= + +# Build options +default_zoom=8 +default_storage=fls0 + +# Installation directories +root='' +prefix='${root}/usr' +bindir='${prefix}/bin' +mandir='${prefix}/share/man' + +# Installation options +install_manpages=yes + +#******************************************************************************# +# Help message # +#******************************************************************************# +usage() { +cat </dev/null ]; then + echo "--default-zoom: a number is expected (got \"$zoom\")" >&2 + elif [ $zoom -lt 1 ]; then + echo "--default-zoom: should be 1 or more (got $zoom)" >&2 + elif [ $zoom -gt 16 ]; then + echo "--default-zoom: should be 16 or less (got $zoom)" >&2 + else default_zoom=$zoom; fi ;; +--default-storage=*) + storage="${arg#*=}" + # check if 4 chars long + if [ ! $(echo "$storage" | wc -c ) -eq 5 ]; then + echo "$0: --default-storage: must be 4 characters long" + continue + fi + # then set + default_storage="$storage" ;; +--noinstall-manpages) install_manpages= ;; +--root=*) root="${arg#*=}" ;; +--prefix=*) prefix="${arg#*=}" ;; +--bindir=*) bindir="${arg#*=}" ;; +--mandir=*) mandir="${arg#*=}" ;; +*) echo "$arg: didn't read" ;; +esac; done + +#******************************************************************************# +# Evaluate variables # +#******************************************************************************# +for var in prefix bindir mandir; do + eval $var'='$(eval 'echo $'$var) +done + +#******************************************************************************# +# Create Makefile configuration # +#******************************************************************************# +exec 3>&1 1>Makefile.cfg +cat <&3 3>&- +chmod +x Makefile.cfg + +#******************************************************************************# +# Finish # +#******************************************************************************# +echo "Configuration loaded, you can make now." + +# End of file. diff --git a/doc/p7.1.txt b/doc/p7.1.txt new file mode 100644 index 0000000..cc5d75e --- /dev/null +++ b/doc/p7.1.txt @@ -0,0 +1,81 @@ +P7(1) +===== +Thomas "Cakeisalie5" Touhey +:Email: thomas@touhey.fr +:man source: p7 +:man manual: p7 manual + +NAME +---- +p7 - command-line utility to communicate with casio calculators + +SYNOPSIS +-------- +[source,bash] +---- +p7 [-h|--help] [-v|--version] [ []] +---- + +DESCRIPTION +----------- +p7 is a command-line utility to communicate with machines using casio's +communication protocol 7.00, like (almost only, not gonna lie) CASIO +fx calculators. For now, it only implements file sending/getting functions +on flash memories. + +Available submenus are: + +*send [-f] [-o oncalc.ext] [-d oncalcdir] local.ext*:: + Send a file to the calculator. +*get [-o local.ext] [-d oncalcdir] oncalc.ext*:: + Get a file from calculator. +*copy [-d sourcedir] [-t destdir] source.ext dest.ext*:: + Copies a file into another on the calculator. +*del [-d oncalcdir] oncalc.ext*:: + Delete a file on the calculator. +*ls*:: + List files on the calculator. +*optimize*:: + Optimize the distant filesystem (defragment). +*info*:: + Dumps information about the calculator. + +OPTIONS +------- +Options start with one or two dashes. Some of the options require an additional +value next to them. + +*-h, --help*:: + Display command/subcommand help page and quit. +*-v, --version*:: + Display version and quit. +*--device /dev/mydevice0*:: + The device with which to interact, usually */dev/ttyUSBx*. + Will use the first device it finds by default. +*--storage abc0*:: + The storage device with which to interact. +*--no-term, --no-exit*:: + Do not terminate connection when action is completed. +*--no-start, --no-init*:: + Do not initialize connection -- should only be used when + *--no-term*/*--no-exit* was used last time p7 was called. +*-d DIR, --directory=DIR*:: + The on-calc directory to work in. + Default for this option is device root. +*-t DDIR, --to=DIR*:: + The destination directory (for file copying/moving). + Default for this option is device root. +*-o OUT, --output=OUT*:: + For the *get* subcommand, the local file path to store in the output (or + '-' for stdout). For the *send* subcommand, the distant filename. For the + two subcommands, default is adapted from the input parameter. +*-f, --force*:: + For the *send* subcommand, force overwrite (default is interactive + confirmation from user). +*-#*:: + Displays a nice little loading bar for *send* and *get* subcommands. If + command is *get* and output is stdout, loading bar won't be displayed. + +SEE ALSO +-------- +*libp7*(3) diff --git a/doc/p7screen.1.txt b/doc/p7screen.1.txt new file mode 100644 index 0000000..9ff134d --- /dev/null +++ b/doc/p7screen.1.txt @@ -0,0 +1,38 @@ +P7SCREEN(1) +=========== +Thomas "Cakeisalie5" Touhey +:Email: thomas@touhey.fr +:man source: p7screen +:man manual: p7screen manual + +NAME +---- +p7screen - display streamed screen from casio fx calculator + +SYNOPSIS +-------- +[source,bash] +---- +p7screen [-h|--help] [-v|--version] +---- + +DESCRIPTION +----------- +p7screen is a command-line utility to display streamed screen from casio fx +calculator, using its communication protocol 7.00 and screenstreaming mode. + +OPTIONS +------- +Options start with one or two dashes. Some of the options require an additional +value next to them. + +*-h, --help*:: + Display help page and quit. +*-v, --version*:: + Display version and quit. +*-z ZOOM, --zoom=ZOOM*:: + Change the zoom (will change the window size too). + +SEE ALSO +-------- +*libp7*(3) diff --git a/src/p7/args.c b/src/p7/args.c new file mode 100644 index 0000000..aff59fd --- /dev/null +++ b/src/p7/args.c @@ -0,0 +1,334 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* args.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7 | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/08/21 19:10:17 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#include +#include +#include +#include + +/* ************************************************************************** */ +/* Help and version messages */ +/* ************************************************************************** */ +/* Version message */ +static const char version_message[] = +"P7 v" QUOTE(VERSION) " (licensed under " QUOTE(LICENSE) ")\n" +"Made by " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">.\n" +"\n" +"This is free software; see the source for copying conditions.\n" +"There is NO warranty; not even for MERCHANTABILITY or\n" +"FITNESS FOR A PARTICULAR PURPOSE."; + +/* Main help message */ +static const char help_main[] = +"Usage: p7 [--version|-v] [--help|-h] [--no-init] [--no-exit]\n" +" [--storage abc0] [--device /dev/omg0]\n" +" [options...]\n" +"\n" +"Subcommands you can use are:\n" +" send Send a file to the calculator.\n" +" get Get a file from the calculator.\n" +" copy Copy a file into another on the calculator.\n" +" del Delete a file on the calculator.\n" +" reset Reset the flash memory.\n" +" optimize Optimize the distant filesystem.\n" +" ls List files on the distant filesystem.\n" +" info Dump info about the calculator.\n" +"\n" +"General options:\n" +" -h, --help Display the help page of the (sub)command and quit.\n" +" -v, --version Display the version message and quit.\n" +" --device dev The calculator device (ttyUSBx).\n" +" By default, will use the first appropriate device found.\n" +" --storage abc0 The storage device with which to interact (fls0, crd0).\n" +" Default storage device is '" QUOTE(DEFAULT_STORAGE) "'.\n" +" --no-exit Does not terminate connection when action is completed.\n" +" --no-init Does not initialize connection (should only be used\n" +" when --no-exit was used last time p7 was called).\n" +"\n" +"Type \"p7 --help\" for some help about the subcommand.\n" +"Report bugs to " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">."; + +/* Sending help message */ +static const char help_send[] = +"Usage: p7 send [-f] [-o ]\n" +" [-d ] [-#] \n" +"Send a file to the calculator.\n" +"\n" +"Options are:\n" +" -f, --force Overwrite without asking\n" +" -o The output filename on the calculator (by default, the same\n" +" as the local file)\n" +" -d The directory on-calc in which the file will be stored (by\n" +" default, the root directory)\n" +" -# Display a nice little loading bar\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Getting help message */ +static const char help_get[] = +"Usage: p7 get [-o ]\n" +" [-d ] \n" +"Request a file from the calculator.\n" +"\n" +"Options are:\n" +" -o The output filename (by default, the same as the on-calc file)\n" +" -d The directory on-calc in which to get the file (by default,\n" +" the root directory)\n" +" -# Display a nice little loading bar (if output isn't stdout)\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Copying help message */ +static const char help_copy[] = +"Usage: p7 copy [-f] [-d ] \n" +"Copies a file into the other on the calculator.\n" +"\n" +"Options are:\n" +" -d The source directory (by default, the root directory)\n" +" -t The dest. directory (by default, the root directory)\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Deleting help message */ +static const char help_del[] = +"Usage: p7 del [-d \n" +"Delete a file on the calculator.\n" +"\n" +"Options are:\n" +" -d The directory on-calc in which to remove the file (by default,\n" +" the root directory)\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Listing help message */ +static const char help_ls[] = +"Usage: p7 ls\n" +"List files on the distant filesystem.\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Resetting help message */ +static const char help_reset[] = +"Usage: p7 reset\n" +"Reset the distant filesystem.\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Optimizing help message */ +static const char help_optimize[] = +"Usage: p7 optimize\n" +"Optimize the distant filesystem.\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* Dumping help message */ +static const char help_info[] = +"Usage: p7 info\n" +"Dump information about the calculator.\n" +"\n" +"Type \"p7 --help\" for other subcommands and general options."; + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/* useful macros */ +#define sub_init(CMD, NARGS) \ + args->menu = mn_##CMD; \ + if (help || aac != 1 + (NARGS)) { \ + puts(help_##CMD); \ + return (0); \ + } + +/** + * parse_args: + * Args parsing main function. + * + * Was my very first experiment with getopt. + * Then I took an arrow in the knee. + * + * @arg ac the arguments count + * @arg av the arguments values + * @arg args the parsed args pointer + * @return if it was successfully parsed + */ + +int parse_args(int ac, char **av, args_t *args) +{ + /* initialize args */ + *args = (args_t){ + .menu = 0, + .nicedisp = 0, + .dirname = NULL, .filename = NULL, + .newdir = NULL, .newname = NULL, + .local = NULL, .force = 0, + .device = NULL, + .storage = QUOTE(DEFAULT_STORAGE), + .noinit = 0, .noexit = 0}; + + /* define options */ + char short_options[] = "hvfo:d:t:#"; + struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"device", required_argument, NULL, 'p'}, + {"storage", required_argument, NULL, 's'}, + {"force", no_argument, NULL, 'f'}, + {"output", required_argument, NULL, 'o'}, + {"directory", required_argument, NULL, 'd'}, + {"to", required_argument, NULL, 't'}, + {"no-init", no_argument, NULL, 'i'}, + {"no-start", no_argument, NULL, 'i'}, + {"no-exit", no_argument, NULL, 'e'}, + {"no-term", no_argument, NULL, 'e'}, + {NULL, 0, NULL, 0} + }; + + /* get all options */ + int c; opterr = 0; + int help = 0; + const char *s_out = NULL, *s_dir = NULL, *s_todir = NULL; + while ((c = getopt_long(ac, av, short_options, long_options, NULL)) != -1) { + switch (c) { + /* help */ + case 'h': help = 1; break; + /* version */ + case 'v': puts(version_message); return (0); break; + /* force */ + case 'f': args->force = 1; break; + /* nice display */ + case '#': args->nicedisp = 1; break; + + /* output file (on calc or local) */ + case 'o': s_out = optarg; break; + /* directory name */ + case 'd': s_dir = optarg; break; + /* destination directory name */ + case 't': s_todir = optarg; break; + + /* device */ + case 'p': args->device = optarg; break; + /* storage */ + case 's': args->storage = optarg; break; + /* force no initialization */ + case 'i': args->noinit = 1; break; + /* force no exit */ + case 'e': args->noexit = 1; break; + + /* in case of error */ + case '?': + if (optopt == 'o') + log("-o, --output: expected an argument\n"); + else if (optopt == 'd') + log("-d, --directory: expected an argument\n"); + else if (optopt == 't') + log("-t, --to: expected an argument\n"); + else if (optopt == 'p') + log("--device: expected an argument\n"); + else if (optopt == 's') + log("--storage: expected an argument\n"); + else + break; + return (0); + break; + } + } + + /* get non-option arguments (subcommand and parameters) */ + int aac = ac - optind; + char **aav = &av[optind]; + + /* get subcommand and things to check */ + char fpmode[2] = " "; + args->localpath = NULL; + /* - all subcommands - */ + if (!aac || !strcmp(aav[0], "help")) { + puts(help_main); + return (0); + } else if (!strcmp(aav[0], "info")) { + sub_init(info, 0) + } else if (!strcmp(aav[0], "ls")) { + sub_init(ls, 0) + } else if (!strcmp(aav[0], "reset")) { + sub_init(reset, 0) + } else if (!strcmp(aav[0], "optimize")) { + sub_init(optimize, 0) + } else if (!strcmp(aav[0], "send")) { + sub_init(send, 1) + + /* put arguments to check */ + fpmode[0] = 'r'; + args->localpath = aav[1]; + args->dirname = s_dir ? s_dir : NULL; + if (s_out) args->filename = s_out; + else { + char *rs = strrchr(args->localpath, '/'); + args->filename = rs ? rs + 1 : args->localpath; + } + } else if (!strcmp(aav[0], "get")) { + sub_init(get, 1) + + /* put arguments to check */ + fpmode[0] = 'w'; + args->filename = aav[1]; + args->dirname = s_dir ? s_dir : NULL; + args->localpath = s_out ? s_out : args->filename; + } else if (!strcmp(aav[0], "copy")) { + sub_init(copy, 2) + + /* get filename */ + args->filename = aav[1]; + args->dirname = s_dir ? s_dir : NULL; + args->newname = aav[2]; + args->newdir = s_todir ? s_todir : NULL; + } else if (!strcmp(aav[0], "del")) { + sub_init(del, 1) + + /* get filename */ + args->filename = aav[1]; + args->dirname = s_dir ? s_dir : NULL; + } else { + /* unknown subcommand ! */ + log("Unknown subcommand '%s'.\n", aav[0]); + return (0); + } + + /* check string lengths */ + int noerror = 0; + if (args->filename && strnlen(args->filename, 13) == 13) + log("On-calc filename must have 12 chars or less!\n"); + else if (args->newname && strnlen(args->newname, 13) == 13) + log("Destination filename must have 12 chars or less!\n"); + else if (args->dirname && strnlen(args->dirname, 9) == 9) + log("On-calc directory name must have 8 chars or less!\n"); + else if (args->newdir && strnlen(args->newdir, 9) == 9) + log("Destination directory name must have 8 chars or less!\n"); + else if (strnlen(args->storage, 5) != 4) + log("Storage device (%s) should be 4 chars long!\n", args->storage); + else + noerror = 1; + if (!noerror) return (0); + + /* check local path */ + if (args->localpath) { + if (fpmode[0] == 'w' && !strcmp(args->localpath, "-")) + args->local = stdout; + else if (!(args->local = fopen(args->localpath, fpmode))) { + log("Could not open local file : %s\n", strerror(errno)); + if (fpmode[0] == 'w') + unlink(args->localpath); + return (0); + } + } + + /* everything went well */ + return (1); +} diff --git a/src/p7/dump.c b/src/p7/dump.c new file mode 100644 index 0000000..25d7d59 --- /dev/null +++ b/src/p7/dump.c @@ -0,0 +1,77 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* dump.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7 | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/08/26 20:14:25 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#define info (response->ack) + +/** + * dump: + * Dump calculator information. + * + * @arg handle the libp7 handle + * @return the error code (0 if ok). + */ + +int dump(p7_handle_t *handle) +{ + /* get response buffer */ + const p7_packet_t *response = p7_get_response(handle); + + /* send get info command and check response */ + int err; + if ((err = p7_send_cmdsys_getinfo(handle))) + return (err); + if (response->type != p7_pt_ack || !response->ack.extended) + return (p7_error_unknown); + + /* dump information */ + /* - wiped out things - */ + if (info.preprog_rom_wiped) + log("Warning: Preprogrammed ROM information looks wiped out !\n"); + if (info.bootcode_wiped) + log("Warning: Bootcode information looks wiped out !\n"); + + /* - preprogrammed ROM - */ + if (!info.preprog_rom_wiped) { + printf("Preprogrammed ROM capacity : %luo\n", info.preprog_rom_capacity); + printf("Preprogrammed ROM version : %02u.%02u.%02u\n", + info.preprog_rom_version.major, info.preprog_rom_version.minor, + info.preprog_rom_version.rev); + printf("Preprogrammed ROM type : \"%s\"\n", info.preprog_rom_version.type); + } + /* - ROM - */ + printf("ROM capacity : %luo\n", info.flash_rom_capacity); + /* - RAM - */ + printf("RAM capacity : %luo\n", info.ram_capacity); + /* - Bootcode - */ + if (!info.bootcode_wiped) { + printf("Bootcode version : %02u.%02u.%02u\n", + info.bootcode_version.major, info.bootcode_version.minor, + info.bootcode_version.rev); + printf("Bootcode type : \"%s\"\n", info.bootcode_version.type); + printf("Bootcode offset : %#08lx\n", info.bootcode_offset); + printf("Bootcode size : %luo\n", info.bootcode_size); + } + /* - OS - */ + printf("OS version : %02u.%02u.%02u\n", + info.os_version.major, info.os_version.minor, info.os_version.rev); + printf("OS type : \"%s\"\n", info.os_version.type); + printf("OS offset : %#08lx\n", info.os_offset); + printf("OS size : %luo\n", info.os_size); + /* - Miscallenous info - */ + printf("Protocol version : %01u.%02u\n", + info.protocol_version.major, info.protocol_version.minor); + printf("Product ID : \"%s\"\n", info.product_id); + printf("Username : \"%s\"\n", info.username); + printf("Hardware ID : \"%s\"\n", info.hwid); + printf("CPU ID : \"%s\"\n", info.cpuid); + + return (0); +} diff --git a/src/p7/main.c b/src/p7/main.c new file mode 100644 index 0000000..a57c7e3 --- /dev/null +++ b/src/p7/main.c @@ -0,0 +1,280 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7 | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/08/21 16:50:46 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#include +#include + +/* ************************************************************************** */ +/* Error messages */ +/* ************************************************************************** */ +/* Couldn't initialize connexion with the calculator. */ +static const char error_noconnexion[] = +"Could not connect to the calculator.\n" +"- Is it plugged in and in receive mode?\n" +"- Have you tried changing the cable?\n"; + +/* Calculator was disconnected. */ +static const char error_disconnected[] = +"Lost connexion to the calculator!\n" +"Please reconnect the calculator, rerun receive mode and try again.\n"; + +/* Calculator was found but program wasn't allowed to communicate with it. */ +static const char error_noaccess[] = +"Could not get access to the calculator.\n" +"Install the appropriate udev rule, or run as root.\n"; + +/* Command was unsupported. */ +static const char error_unsupported[] = +"The command is unsupported by the calculator.\n" +"- Does the calculator have mass storage?\n" +"- Does its OS allow the use of it?\n" +"- Is it in Receive Mode (and not in OS Update)?\n"; + +/* The device didn't exist. */ +static const char error_unsupported_device[] = +"Device '%s' is not supported by the device.\n"; + +/* The calculator acted in an unplanned way. */ +static const char error_unplanned[] = +"The calculator didn't act as planned.\n" +"Stop receive mode on calculator and start it again before re-running p7.\n" +"Error was: %s\n"; + +/* Requested file didn't exist. */ +static const char error_noexists[] = +"Requested file didn't exist.\n"; + +/* Sent file cannot be empty. */ +static const char error_empty[] = +"Can't send an empty file!\n"; + +/* Not enough space left on the calculator. */ +static const char error_nospace[] = +"Not enough space on the calculator for the file you're trying to send.\n" +"If you believe there should be, try optimizing (OPT) on the calculator\n" +"(in MEMORY menu) and try again.\n"; + +/* ************************************************************************** */ +/* Auxiliary functions */ +/* ************************************************************************** */ +/** + * sendfile_confirm: + * Confirm file sending. + * + * @return if the file overwriting is confirmed + */ + +static int sendfile_display_initialized = 0; +static int sendfile_confirm(void) +{ + /* Print stuff */ + printf("It looks like the file already exists on the calculator.\n"); + printf("Overwrite ? ([n]/y) "); + + /* Get the line */ + char *line = NULL; size_t n; + ssize_t err = getline(&line, &n, stdin); + if (err < 0) return (0); + + /* Check if should overwrite */ + int overwrite = err >= 1 && !strncmp(line, "y", 1); + free(line); + return (overwrite); +} + +/** + * sendfile_display: + * File sending nice display. + * + * "Initialization" is when id > total (called in main). + * + * @arg id data packet ID + * @arg total total number of data packets + */ + +static void sendfile_display(p7ushort_t id, p7ushort_t total) +{ + /* here's the buffer */ + static char buf[50] = + "\r|---------------------------------------| 00.00%"; + static char *bar = &buf[2]; + + /* initialize */ + static int pos; + + /* if is initialize, fill */ + if (id > total) { + pos = 0; + /* indicate that is has been initialized */ + sendfile_display_initialized = 1; + /* put initial buffer */ + fputs(buf, stdout); + /* save cursor position */ + fputs("\x1B[s", stdout); + /* we're done */ + return ; + } + + /* id and total start from 1, let them start from zero */ + id--; total--; + + /* modify buffer */ + /* - # - */ + int current = 38 * id / total; + while (pos <= current) bar[pos++] = '#'; + /* - % - */ + unsigned int percent = 10000 * id / total; + sprintf(&buf[43], "%02u.%02u", percent / 100, percent % 100); + + /* put it */ + fputs(buf, stdout); + /* force cursor position */ + fputs("\x1B""8", stdout); +} + +/** + * print_file_info: + * File listing callback. + * + * @arg dir the directory in which the file is stored (NULL if root) + * @arg name the filename + * @arg size the filesize + */ + +static void print_file_info(const char *dir, const char *name, p7uint_t size) +{ + /* initialize buffer */ + static char buf[45]; + + /* clean buffer */ + memset(buf, ' ', 28); + /* put path in buffer */ + char *b = buf; + if (dir) b = stpcpy(b, dir), *b++ = '/'; + b = stpcpy(b, name), *b = ' '; + /* put size */ + sprintf(&buf[28], "% 10luo", size); + + /* put the string */ + puts(buf); +} + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * main: + * User entry point of the program. + * + * @arg ac arguments count + * @arg av arguments values + * @return return code (0 if ok) + */ + +int main(int ac, char **av) +{ + /* Parse args */ + args_t args; + if (!parse_args(ac, av, &args)) + return (0); + + /* Initialize libp7 and communication */ + p7_handle_t *handle; int err; + if (args.device) err = p7_cinit(&handle, 1, !args.noinit, args.device, 0); + else err = p7_init(&handle, 1, !args.noinit); + if (err) { + /* display error */ + switch (err) { + case p7_error_nocalc: log(error_noconnexion); break; + case p7_error_noaccess: log(error_noaccess); break; + default: log(error_unplanned, p7_strerror(err)); break; + } + + /* closing, removing if necessary */ + if (args.menu == mn_send) + fclose(args.local); + if (args.menu == mn_get && args.local != stdout) { + fclose(args.local); + unlink(args.localpath); + } + return (1); + } + + /* Check according to menu */ + switch (args.menu) { + case mn_send: + err = p7_sendfile(handle, args.local, args.dirname, args.filename, + args.storage, 1, args.force ? NULL : &sendfile_confirm, + args.nicedisp ? &sendfile_display : NULL); + break; + case mn_get: + err = p7_reqfile(handle, args.local, args.dirname, args.filename, + args.storage, args.nicedisp && args.local != stdout + ? &sendfile_display : NULL); + if (err) unlink(args.localpath); + break; + case mn_copy: + err = p7_copyfile(handle, args.dirname, args.filename, + args.newdir, args.newname, args.storage); + break; + case mn_del: + err = p7_delfile(handle, args.dirname, args.filename, + args.storage); + break; + case mn_ls: + err = p7_lsfiles(handle, args.storage, &print_file_info); + break; + case mn_reset: + err = p7_resetflash(handle, args.storage); + break; + case mn_optimize: + err = p7_optimize(handle, args.storage); + break; + case mn_info: + err = dump(handle); + break; + } + + /* put error */ + if (err && err != p7_error_denied_overwrite) { + if (sendfile_display_initialized) + puts("\b\b\b\b\b\bError !"); + + /* close the file */ + if (args.local) fclose(args.local); + if (args.menu == mn_get && args.local != stdout) + unlink(args.localpath); + + /* put the error string */ + switch (err) { + case p7_error_fullmem: log(error_nospace); break; + case p7_error_empty: log(error_empty); break; + case p7_error_notfound: log(error_noexists); break; + case p7_error_nocalc: log(error_disconnected); break; + case p7_error_unsupported: log(error_unsupported); break; + case p7_error_unsupported_device: + log(error_unsupported_device, args.device); break; + default: log(error_unplanned, p7_strerror(err)); + } + + /* return that an error has occured */ + return (1); + } + + puts("\b\b\b\b\b\bTransfer complete."); + if (args.local) fclose(args.local); + + /* terminate communication and de-initialize libp7 */ + p7_exit(handle, !args.noexit); + + /* Then we're good */ + return (0); +} diff --git a/src/p7/main.h b/src/p7/main.h new file mode 100644 index 0000000..8f9b1de --- /dev/null +++ b/src/p7/main.h @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.h |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7 | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/08/21 16:50:57 |___/ */ +/* */ +/* ************************************************************************** */ +#ifndef MAIN_H +# define MAIN_H +# define Q(x) #x +# define QUOTE(x) Q(x) +# include +# include +# define log(S, ...) fprintf(stderr, S, ##__VA_ARGS__) + +/* Menus */ +typedef enum { + mn_send, mn_get, mn_copy, mn_del, mn_ls, mn_reset, mn_optimize, + mn_info +} menu_t; + +/* Arguments */ +typedef struct { + /* basic things */ + menu_t menu; + int nicedisp; + + /* for file transferring menus */ + const char *dirname, *filename; + const char *newdir, *newname; + FILE *local; const char *localpath; + int force; + + /* other options */ + const char *device; + const char *storage; + int noinit; + int noexit; +} args_t; + +/* Parsing function */ +int parse_args(int ac, char **av, args_t *args); + +/* Dumping function */ +int dump(p7_handle_t *handle); + +#endif /* MAIN_H */ diff --git a/src/p7screen/args.c b/src/p7screen/args.c new file mode 100644 index 0000000..ee36933 --- /dev/null +++ b/src/p7screen/args.c @@ -0,0 +1,118 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* args.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7screen | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/06 15:35:29 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#include + +/* ************************************************************************** */ +/* Help and version messages */ +/* ************************************************************************** */ +/* Version message */ +static const char version_message[] = +"P7SCREEN v" QUOTE(VERSION) " (licensed under " QUOTE(LICENSE) ")\n" +"Made by " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">.\n" +"\n" +"This is free software; see the source for copying conditions.\n" +"There is NO warranty; not even for MERCHANTABILITY or\n" +"FITNESS FOR A PARTICULAR PURPOSE."; + +/* Help message */ +static const char help_main[] = +"Usage: p7screen [--help|-h] [--version|-v]\n" +" [--device /dev/omg0]\n" +"\n" +"Displays the streamed screen from a CASIO fx calculator.\n" +"\n" +"Options are:\n" +" -h, --help Display this help page\n" +" -v, --version Displays the version\n" +" --device dev The calculator device (/dev/ttyUSBx).\n" +" By default, will use the first appropriate device found.\n" +" -z ZOOM Change the zoom (1 to 16)\n" +" By default, the zoom will be " QUOTE(DEFAULT_ZOOM) ".\n" +"\n" +"Report bugs to " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">."; + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * parse_args: + * Args parsing main function. + * + * Inspired of the edits of my first experiment with getopt. + * Interesting, huh? + * + * @arg ac the arguments count + * @arg av the arguments values + * @arg device pointer to the device + * @arg zoom pointer the zoom + * @arg args the parsed args pointer + * @return 0 if ok, other if not. + */ + +int parse_args(int ac, char **av, const char **device, int *zoom) +{ + /* initialize args */ + *device = NULL; + *zoom = DEFAULT_ZOOM; + + /* define options */ + const char short_options[] = "hvz:"; + const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"device", required_argument, NULL, 'd'}, + {"zoom", required_argument, NULL, 'z'}, + {NULL, 0, NULL, 0} + }; + + /* get all options */ + int c; opterr = 0; + int help = 0, version = 0; + while ((c = getopt_long(ac, av, short_options, long_options, NULL)) != -1) { + switch (c) { + /* help */ + case 'h': help = 1; break; + /* version */ + case 'v': version = 1; break; + /* device */ + case 'd': *device = optarg; break; + /* zoom */ + case 'z': + *zoom = atoi(optarg); + if (*zoom <= 0 || *zoom > 16) { + log("-z, --zoom: should be between 1 and 16"); + return (1); + } + break; + + /* error (ignore) */ + case '?': + if (optopt == 'z') + log("-z, --zoom: expected an argument\n"); + else if (optopt == 'd') + log("--device: expected an argument\n"); + else + break; + return (1); + } + } + + /* check if there is any parameter */ + if (ac - optind) + help = 1; + + /* print help or version if required, and return */ + if (version) puts(version_message); + else if (help) puts(help_main); + else return (0); + return (1); +} diff --git a/src/p7screen/main.c b/src/p7screen/main.c new file mode 100644 index 0000000..1ad9e22 --- /dev/null +++ b/src/p7screen/main.c @@ -0,0 +1,166 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7screen | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/06 15:35:22 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#include +#include + +/* ************************************************************************** */ +/* Error messages */ +/* ************************************************************************** */ +/* Couldn't initialize connexion to calculator. */ +static const char error_noconnexion[] = +"Could not connect to the calculator.\n" +"- Is it plugged in and in PROJ mode?\n" +"- Have you tried unplugging, plugging and selecting Projector on pop-up?\n" +"- Have you tried changing the cable?\n"; + +/* Calculator was found but program wasn't allowed to communicate with it. */ +static const char error_noaccess[] = +"Could not get access to the calculator.\n" +"Install the appropriate udev rule, or run as root.\n"; + +/* The calculator acted in a weird way. */ +static const char error_unplanned[] = +"The calculator didn't act as planned.\n" +"Stop receive mode on calculator and start it again before re-running p7screen.\n" +"Error was: %s\n"; + +/* ************************************************************************** */ +/* Globals */ +/* ************************************************************************** */ +/* The z00m (omG) */ +static int zoom; + +/* ************************************************************************** */ +/* Auxiliary functions */ +/* ************************************************************************** */ +/** + * display_callback: + * The main callback for screen streaming. + * + * @arg w the width of the received image + * @arg h the height of the received image + * @arg pixels the image data + * @return if reception should continue + */ + +static int display_callback(int w, int h, uint32_t **pixels) +{ + /* create screen if there isn't one */ + static SDL_Surface *screen = NULL; + static int saved_w = 0, saved_h = 0; + if (!screen || saved_w != w || saved_h != h) { + /* create the window */ + if (!(screen = SDL_SetVideoMode(w * zoom, h * zoom, 32, + SDL_SWSURFACE | SDL_DOUBLEBUF))) { + log("Couldn't set video mode: %s\n", SDL_GetError()); + return (0); + } + SDL_WM_SetCaption("P7screen", NULL); + + /* save data and display message */ + saved_w = w; saved_h = h; + puts("Turn off your calculator (SHIFT+AC) when you have finished."); + } + + /* edit screen */ + /* - lock it - */ + SDL_LockSurface(screen); + /* - copy - */ + uint32_t *px = (uint32_t*)screen->pixels; + int linesize = w * zoom; + for (int y = 0; y < h; y++) { + uint32_t *refline = px; + for (int x = 0; x < w; x++) { + uint32_t pixel = pixels[y][x]; + for (int zx = 0; zx < zoom; zx++) + *px++ = pixel; + } + for (int zy = 1; zy < zoom; zy++) { + memcpy(px, refline, linesize * sizeof(uint32_t)); + px += linesize; + } + } + /* - unlock it - */ + SDL_UnlockSurface(screen); + + /* update screen */ + SDL_Flip(screen); + + /* check if user has pressed escape or cross */ + SDL_Event event; + SDL_PollEvent(&event); + if ((event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) + || event.type == SDL_QUIT) + return (0); + + /* continue! */ + return (1); +} + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * main: + * Entry point of the program. + * + * @arg ac arguments count + * @arg av arguments values + * @return if it worked (0 if OK) + */ + +int main(int ac, char **av) +{ + /* parse args */ + const char *device; + if (parse_args(ac, av, &device, &zoom)) + return (0); + + /* Initialize SDL */ + if (SDL_Init(SDL_INIT_VIDEO)) { + log("Failed to initialize SDL: %s\n", SDL_GetError()); + return (3); + } + atexit(SDL_Quit); + + /* Initialize libp7 */ + p7_handle_t *handle; int err; + if (device) err = p7_cinit(&handle, 0, 0, device, 0); + else err = p7_init(&handle, 0, 0); + if (err) { + /* display error */ + switch (err) { + case p7_error_nocalc: log(error_noconnexion); break; + case p7_error_noaccess: log(error_noaccess); break; + default: log(error_unplanned, p7_strerror(err)); break; + } + + /* return */ + return (1); + } + + /* receive screen */ + if ((err = p7_getscreen(handle, &display_callback)) + && err != p7_error_nocalc) { + switch (err) { + case p7_error_timeout: log(error_noconnexion); break; + default: log(error_unplanned, p7_strerror(err)); break; + } + return (1); + } + + /* close */ + p7_exit(handle, 0); + + /* everything went well */ + return (0); +} diff --git a/src/p7screen/main.h b/src/p7screen/main.h new file mode 100644 index 0000000..5814d30 --- /dev/null +++ b/src/p7screen/main.h @@ -0,0 +1,20 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.h |_ _|__ _ _| |__ ___ _ _ */ +/* | Project : p7screen | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/06 15:35:37 |___/ */ +/* */ +/* ************************************************************************** */ +#ifndef MAIN_H +# define MAIN_H +# include +# define Q(x) #x +# define QUOTE(x) Q(x) +# define log(S, ...) fprintf(stderr, S, ##__VA_ARGS__) + +/* all functions */ +int parse_args(int ac, char **av, const char **device, int *zoom); + +#endif /* MAIN_H */