diff --git a/C-functions_overload.c b/C-functions_overload.c new file mode 100644 index 0000000..a7ffac6 --- /dev/null +++ b/C-functions_overload.c @@ -0,0 +1,20 @@ +#include +#include + +#define FUNC(arg) _Generic(arg, uint16_t: funcu, int32_t: funci)(arg) + +void funcu(uint16_t arg){ + printf("uint16_t: %u\n", arg); +} + +void funci(int32_t arg){ + printf("int32_t: %d\n", arg); +} + +int main(){ + uint16_t u = 32; + int32_t i = -50333; + FUNC(u); + FUNC(i); + return 0; +} diff --git a/README b/README index 70ee00e..8d06b2d 100644 --- a/README +++ b/README @@ -2,12 +2,20 @@ This is a set of small usefull utilites & macros bidirectional_list - simple list with operation of searching, inserting, deleting + B-trees - simple but slowly binary search trees with all main operations + fifo_lifo - simple stack-like queues + simple_list - 1-directional list with functions: add element; delete list + +tmout - theads-based timeout to check something + usefull_macros - a lot of different macros & functions + * safe memory allocation & freeing * coloured output on tty & monochromeous on non-tty * error/warning/debug macros * MMAP files into memory +stellarium_emul - snippet for stellarium telescope remote control diff --git a/cmakelists_/CMakeLists_regular_01.txt b/cmakelists_/CMakeLists_regular_01.txt index eb9d895..4bc2389 100644 --- a/cmakelists_/CMakeLists_regular_01.txt +++ b/cmakelists_/CMakeLists_regular_01.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.0) set(PROJ tvguide) set(MINOR_VERSION "0") set(MID_VERSION "0") @@ -7,8 +7,15 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") message("VER: ${VERSION}") +project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C CXX) + # default flags -set(CFLAGS -O2 -std=gnu99) +set(CMAKE_C_FLAGS_RELEASE "") +set(CMAKE_C_FLAGS_DEBUG "") +set(CMAKE_CXX_FLAGS_RELEASE "") +set(CMAKE_CXX_FLAGS_DEBUG "") +set(CMAKE_C_FLAGS "-O2 -std=gnu99") +set(CMAKE_CXX_FLAGS "-O2") set(CMAKE_COLOR_MAKEFILE ON) @@ -18,28 +25,31 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) # we can change file list #if(NOT DEFINED something) -# set(SOURCES ${SOURCES} one_more_list) -# add_definitions(-DSOME_DEFS) +# set(SOURCES ${SOURCES} one_more_list) +# add_definitions(-DSOME_DEFS) #endif() # cmake -DEBUG=1 -> debugging if(DEFINED EBUG) - set(CFLAGS ${CFLAGS} -Wextra -Wall -Werror -W) - add_definitions(-DEBUG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_BUILD_TYPE DEBUG) + add_definitions(-DEBUG) +else() + set(CMAKE_BUILD_TYPE RELEASE) endif() set(MODULES libavformat libavcodec libswscale libavutil libavdevice) # additional modules on condition #if(DEFINED SOMETHING) -# set(MODULES ${MODULES} more_modules>=version) -# add_definitions(-DSOMEDEFS) +# set(MODULES ${MODULES} more_modules>=version) +# add_definitions(-DSOMEDEFS) #endif() -project(${PROJ}) # change wrong behaviour with install prefix if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local") - message("Change default install path to /usr") - set(CMAKE_INSTALL_PREFIX "/usr") + message("Change default install path to /usr") + set(CMAKE_INSTALL_PREFIX "/usr") endif() message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") @@ -47,11 +57,11 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) if(NOT DEFINED LOCALEDIR) - if(DEFINED DEBUG) - set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale) - else() - set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) - endif() + if(DEFINED DEBUG) + set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale) + else() + set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) + endif() endif() # gettext files @@ -68,15 +78,15 @@ find_package(PkgConfig REQUIRED) # set(${${PROJ}_LIBRARY_DIRS} ...) if(MODULES) - pkg_check_modules(${PROJ} REQUIRED ${MODULES}) + pkg_check_modules(${PROJ} REQUIRED ${MODULES}) endif() #OpenMP #include(FindOpenMP) #if(OPENMP_FOUND) -# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") -# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") #endif() # exe file @@ -85,47 +95,47 @@ target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${OPENGL_LIBRARIES} ...) include_directories(${${PROJ}_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR} ...) link_directories(${${PROJ}_LIBRARY_DIRS} ) add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" - -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" - -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" - -DMAJOR_VERSION=\"${MAJOR_VESION}\") + -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" + -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" + -DMAJOR_VERSION=\"${MAJOR_VESION}\") # Installation of the program if(NOT DEFINED DEBUG) - INSTALL(FILES ${MO_FILE} DESTINATION "share/locale/ru/LC_MESSAGES") - #PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) - INSTALL(TARGETS ${PROJ} DESTINATION "bin") - #PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - # Script to be executed at installation time (kind of post-intallation script) to - # change the right accesses on the installed files - #INSTALL(SCRIPT inst.cmake) + INSTALL(FILES ${MO_FILE} DESTINATION "share/locale/ru/LC_MESSAGES") + #PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + INSTALL(TARGETS ${PROJ} DESTINATION "bin") + #PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + # Script to be executed at installation time (kind of post-intallation script) to + # change the right accesses on the installed files + #INSTALL(SCRIPT inst.cmake) else() - install(CODE "MESSAGE(\"Don't install in DEBUG mode! First run cmake without -DEBUG defined.\")") + install(CODE "MESSAGE(\"Don't install in DEBUG mode! First run cmake without -DEBUG defined.\")") endif(NOT DEFINED DEBUG) find_package(Gettext REQUIRED) find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE) - message(FATAL_ERROR "xgettext not found") + message(FATAL_ERROR "xgettext not found") endif() file(MAKE_DIRECTORY ${LCPATH}) file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES) add_custom_command( - OUTPUT ${PO_FILE} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} - COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} - COMMAND enconv ${PO_FILE} - DEPENDS ${SOURCES} + OUTPUT ${PO_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} + COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} + COMMAND enconv ${PO_FILE} + DEPENDS ${SOURCES} ) # we need this to prevent ru.po from deleting by make clean add_custom_target( - RU_FILE - COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} - DEPENDS ${PO_FILE} + RU_FILE + COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} + DEPENDS ${PO_FILE} ) add_custom_command( - OUTPUT ${MO_FILE} - COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE} - DEPENDS ${PO_FILE} + OUTPUT ${MO_FILE} + COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE} + DEPENDS ${PO_FILE} ) diff --git a/cmakelists_/CMakeLists_regular_02.txt b/cmakelists_/CMakeLists_regular_02.txt index 46807c8..118a9ca 100644 --- a/cmakelists_/CMakeLists_regular_02.txt +++ b/cmakelists_/CMakeLists_regular_02.txt @@ -1,11 +1,12 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.0) set(PROJ fitsread) set(MINOR_VERSION "1") set(MID_VERSION "0") set(MAJOR_VERSION "0") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") -enable_language(C) +project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C CXX) +#enable_language(C) message("VER: ${VERSION}") # threads number definition @@ -21,7 +22,12 @@ add_definitions(-DTHREAD_NUMBER=${PROCESSOR_COUNT}) message("In multithreaded operations will use ${PROCESSOR_COUNT} threads") # default flags -set(CFLAGS -O2 -Wextra -Wall -Werror -W -Wno-trampolines -std=gnu99) +set(CMAKE_C_FLAGS_RELEASE "") +set(CMAKE_C_FLAGS_DEBUG "") +set(CMAKE_CXX_FLAGS_RELEASE "") +set(CMAKE_CXX_FLAGS_DEBUG "") +set(CMAKE_C_FLAGS "-O2 -std=gnu99") +set(CMAKE_CXX_FLAGS "-O2") set(CMAKE_COLOR_MAKEFILE ON) @@ -29,8 +35,13 @@ set(CMAKE_COLOR_MAKEFILE ON) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) # cmake -DDEBUG=1 -> debugging -if(DEFINED DEBUG) +if(DEFINED EBUG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall -Werror -W") + set(CMAKE_BUILD_TYPE DEBUG) add_definitions(-DEBUG) +else() + set(CMAKE_BUILD_TYPE RELEASE) endif() # directory should contain dir locale/ru for gettext translations @@ -64,7 +75,6 @@ endif() ###### additional flags ###### list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads") -project(${PROJ}) # change wrong behaviour with install prefix if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local") message("Change default install path to /usr") diff --git a/kicad-copy/cmdlnopts.c b/kicad-copy/cmdlnopts.c index 676730a..c1a9b28 100644 --- a/kicad-copy/cmdlnopts.c +++ b/kicad-copy/cmdlnopts.c @@ -49,7 +49,7 @@ myoption cmdlnopts[] = { */ glob_pars *parce_args(int argc, char **argv){ int i; - memset(&G, sizeof(glob_pars), 0); // clear all + memset(&G, 0, sizeof(glob_pars)); // clear all // format of help: "Usage: progname [args]\n" change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n"); // parse arguments diff --git a/kicad-copy/copy_pcb.c b/kicad-copy/copy_pcb.c index 9135bfc..ac88d87 100644 --- a/kicad-copy/copy_pcb.c +++ b/kicad-copy/copy_pcb.c @@ -89,15 +89,18 @@ int main(int argc, char **argv){ while((iptr = strstr(iptr, "$Comp\n"))){ DBG("%dth component: ", ++i); iptr = strstr(iptr, "\nL "); - if(!iptr) break; iptr += 3; + if(!iptr) break; + iptr += 3; if(sscanf(iptr, "%s %s\n", comp, val) != 2) continue; DBG("component %s with label %s\n", comp, val); iptr = strstr(iptr, "\nU "); - if(!iptr) break; iptr += 3; + if(!iptr) break; + iptr += 3; if(sscanf(iptr, "%d %d %s\n",&N1,&N2,comp) != 3) continue; DBG("N1 = %d; N2 = %d; comp label: %s\n",N1,N2,comp); iptr = strstr(iptr, "\nF"); // go to line "F 0" - if(!iptr) break; iptr++; + if(!iptr) break; + iptr++; // printout all what was before: oldptr = printdata(oldptr, iptr); for(j = 0; j < L; j++){ diff --git a/stellarium_emul/Makefile b/stellarium_emul/Makefile new file mode 100644 index 0000000..f72d4ce --- /dev/null +++ b/stellarium_emul/Makefile @@ -0,0 +1,22 @@ +PROGRAM = stellariumdaemon +#LDFLAGS = -lcrypt -lm +SRCS = $(wildcard *.c) +CC = gcc +DEFINES = -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=1111 -DEBUG +CXX = gcc +CFLAGS = -Wall -Werror -Wextra -Wno-trampolines $(DEFINES) +OBJS = $(SRCS:.c=.o) +all : $(PROGRAM) +$(PROGRAM) : $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +# some addition dependencies +# %.o: %.c +# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@ +#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS) +# @touch $@ + +clean: + /bin/rm -f *.o *~ +depend: + $(CXX) -MM $(CXX.SRCS) diff --git a/stellarium_emul/Readme b/stellarium_emul/Readme new file mode 100644 index 0000000..d81b392 --- /dev/null +++ b/stellarium_emul/Readme @@ -0,0 +1 @@ +Stellarium control of 10-micron mount diff --git a/stellarium_emul/cmdlnopts.c b/stellarium_emul/cmdlnopts.c new file mode 100644 index 0000000..580fc49 --- /dev/null +++ b/stellarium_emul/cmdlnopts.c @@ -0,0 +1,96 @@ +/* geany_encoding=koi8-r + * cmdlnopts.c - the only function that parse cmdln args and returns glob parameters + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ +#include +#include +#include +#include +#include +#include "cmdlnopts.h" +#include "usefull_macros.h" + +/* + * here are global parameters initialisation + */ +int help; +glob_pars G; + +#define DEFAULT_COMDEV "/dev/ttyUSB0" +// port for connections +#define DEFAULT_PORT "10000" +// accept only local connections +//#define ACCEPT_IP "192.168.3.225" +// default PID filename: +#define DEFAULT_PIDFILE "/tmp/stellariumdaemon.pid" + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .device = DEFAULT_COMDEV, + .port = DEFAULT_PORT, + .pidfile = DEFAULT_PIDFILE, + .emulation = 0, + .logfile = NULL // don't save logs +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"device", NEED_ARG, NULL, 'i', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")}, + {"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&G.emulation), _("run in emulation mode")}, + //{"hostname",NEED_ARG, NULL, 'H', arg_string, APTR(&G.hostname), _("hostname to connect (default: localhost)")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")}, + {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, + {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to connect (default: " DEFAULT_PORT ")")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + int i; + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + G.rest_pars_num = argc; + G.rest_pars = calloc(argc, sizeof(char*)); + for (i = 0; i < argc; i++) + G.rest_pars[i] = strdup(argv[i]); + } + return &G; +} + diff --git a/stellarium_emul/cmdlnopts.h b/stellarium_emul/cmdlnopts.h new file mode 100644 index 0000000..cfcafee --- /dev/null +++ b/stellarium_emul/cmdlnopts.h @@ -0,0 +1,43 @@ +/* geany_encoding=koi8-r + * cmdlnopts.h - comand line options for parceargs + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ + +#pragma once +#ifndef __CMDLNOPTS_H__ +#define __CMDLNOPTS_H__ + +#include "parseargs.h" + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *device; // serial device name + char *port; // port to connect + char *pidfile; // name of PID file + char *logfile; // logging to this file + int emulation; // run in emulation mode + int rest_pars_num; // number of rest parameters + char** rest_pars; // the rest parameters: array of char* +} glob_pars; + + +glob_pars *parse_args(int argc, char **argv); +#endif // __CMDLNOPTS_H__ diff --git a/stellarium_emul/daemon.c b/stellarium_emul/daemon.c new file mode 100644 index 0000000..2a457f2 --- /dev/null +++ b/stellarium_emul/daemon.c @@ -0,0 +1,141 @@ +/* + * daemon.c - functions for running in background like a daemon + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ + +#define PROC_BASE "/proc" + +#include // printf, fopen, ... +#include // getpid +#include // perror +#include // opendir +#include // opendir +#include // stat +#include // fcntl +#include // exit +#include // memset + +/** + * read process name from /proc/PID/cmdline + * @param pid - PID of interesting process + * @return filename or NULL if not found + * don't use this function twice for different names without copying + * its returning by strdup, because `name` contains in static array + */ +char *readname(pid_t pid){ + static char name[256]; + char *pp = name, byte, path[256]; + FILE *file; + int cntr = 0; + size_t sz; + snprintf (path, 255, PROC_BASE "/%d/cmdline", pid); + file = fopen(path, "r"); + if(!file) return NULL; // there's no such file + do{ // read basename + sz = fread(&byte, 1, 1, file); + if(sz != 1) break; + if(byte != '/') *pp++ = byte; + else{ + pp = name; + cntr = 0; + } + }while(byte && cntr++ < 255); + name[cntr] = 0; + fclose(file); + return name; +} + +void iffound_default(pid_t pid){ + fprintf(stderr, "\nFound running process (pid=%d), exit.\n", pid); + exit(0); +} + +/** + * check wether there is a same running process + * exit if there is a running process or error + * Checking have 3 steps: + * 1) lock executable file + * 2) check pidfile (if you run a copy?) + * 3) check /proc for executables with the same name (no/wrong pidfile) + * @param selfname - argv[0] or NULL for non-locking + * @param pidfilename - name of pidfile or NULL if none + * @param iffound - action to run if file found or NULL for exit(0) + */ +void check4running(char *selfname, char *pidfilename, void (*iffound)(pid_t pid)){ + DIR *dir; + FILE *pidfile, *fself; + struct dirent *de; + struct stat s_buf; + pid_t pid = 0, self; + struct flock fl; + char *name, *myname; + if(!iffound) iffound = iffound_default; + if(selfname){ // block self + fself = fopen(selfname, "r"); // open self binary to lock + memset(&fl, 0, sizeof(struct flock)); + fl.l_type = F_WRLCK; + if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking + perror("fcntl"); + exit(1); + } + if(fl.l_type != F_UNLCK){ // file is locking - exit + printf("Found locker, PID = %d!\n", fl.l_pid); + exit(1); + } + fl.l_type = F_RDLCK; + if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){ + perror("fcntl"); + exit(1); + } + } + self = getpid(); // get self PID + if(!(dir = opendir(PROC_BASE))){ // open /proc directory + perror(PROC_BASE); + exit(1); + } + if(!(name = readname(self))){ // error reading self name + perror("Can't read self name"); + exit(1); + } + myname = strdup(name); + if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists + pidfile = fopen(pidfilename, "r"); + if(pidfile){ + if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process + if((name = readname(pid)) && strncmp(name, myname, 255) == 0) + iffound(pid); + } + fclose(pidfile); + } + } + // There is no pidfile or it consists a wrong record + while((de = readdir(dir))){ // scan /proc + if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self + continue; + if((name = readname(pid)) && strncmp(name, myname, 255) == 0) + iffound(pid); + } + closedir(dir); + if(pidfilename){ + pidfile = fopen(pidfilename, "w"); + fprintf(pidfile, "%d\n", self); // write self PID to pidfile + fclose(pidfile); + } + free(myname); +} diff --git a/stellarium_emul/emulation.c b/stellarium_emul/emulation.c new file mode 100644 index 0000000..a96640f --- /dev/null +++ b/stellarium_emul/emulation.c @@ -0,0 +1,103 @@ +/* + * geany_encoding=koi8-r + * emulation.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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. + * + */ +#include "math.h" +#include "emulation.h" +#include "usefull_macros.h" + +// emulation speed over RA & DEC (0.5 degr per sec) +#define RA_SPEED (0.033) +#define DECL_SPEED (0.5) + +// current coordinates +static double RA = 0., DECL = 0.; +// target coordinates +static double RAtarg = 0., DECLtarg = 0.; +// coordinates @ guiding start +static double RA0 = 0., DECL0 = 0.; +static double raspeed = 0.; +// ==1 if pointing +static int pointing = 0; +// pointing start time +static double tstart = -1.; + +/** + * send coordinates to telescope emulation + * @param ra - right ascention (hours) + * @param decl - declination (degrees) + * @return 1 if all OK + */ +int point_emulation(double ra, double decl){ + DBG("(emul) Send ra=%g, decl=%g", ra, decl); + putlog("(emul) Send ra=%g, decl=%g", ra, decl); + RAtarg = ra; DECLtarg = decl; + RA0 = RA; DECL0 = DECL; + raspeed = (RAtarg > RA) ? RA_SPEED : -RA_SPEED; + if(fabs(RAtarg - RA) > 12.){ // go to opposite direction + raspeed = -raspeed; + } + tstart = dtime(); + pointing = 1; + return 0; +} + +static double getradiff(){ + double diff = RAtarg - RA; + if(raspeed < 0.) diff = -diff; + if(diff > 12.) diff -= 24.; + else if(diff < -12.) diff += 24.; + return fabs(diff); +} + +/** + * get coordinates (emulation) + * @return 1 if all OK + */ +int get_emul_coords(double *ra, double *decl){ + if(pointing){ + DBG("RA/DEC: targ: %g/%g, cur: %g/%g, start: %g/%g", RAtarg, DECLtarg, RA, DECL, RA0, DECL0); + // diff < speed? stop + if((fabs(RAtarg - RA) < RA_SPEED && fabs(DECLtarg - DECL) < DECL_SPEED)){ + RA = RAtarg; + DECL = DECLtarg; + pointing = 0; // guiding + DBG("@ target"); + }else{ // calculate new coordinates + double radiff = getradiff(), decldiff = fabs(DECLtarg - DECL); + double tdiff = dtime() - tstart; + RA = RA0 + raspeed * tdiff; + DBG("RA=%g", RA); + if(getradiff() > radiff) RA = RAtarg; + DBG("RA=%g", RA); + if(RA < 0.) RA += 24.; + else if(RA > 24.) RA -= 24.; + DBG("RA=%g", RA); + double sign = (DECLtarg > DECL) ? 1. : -1.; + DECL = DECL0 + sign * DECL_SPEED * tdiff; + if(fabs(DECLtarg - DECL) > decldiff) DECL = DECLtarg; + DBG("RA/DEC: targ: %g/%g, cur: %g/%g, start: %g/%g", RAtarg, DECLtarg, RA, DECL, RA0, DECL0); + } + } + if(ra) *ra = RA; + if(decl) *decl = DECL; + return 1; +} diff --git a/stellarium_emul/emulation.h b/stellarium_emul/emulation.h new file mode 100644 index 0000000..c15fa71 --- /dev/null +++ b/stellarium_emul/emulation.h @@ -0,0 +1,30 @@ +/* + * geany_encoding=koi8-r + * emulation.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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. + * + */ +#pragma once +#ifndef __EMULATION_H__ +#define __EMULATION_H__ + +int point_emulation(double ra, double decl); +int get_emul_coords(double *ra, double *decl); + +#endif // __EMULATION_H__ diff --git a/stellarium_emul/main.c b/stellarium_emul/main.c new file mode 100644 index 0000000..ac73f61 --- /dev/null +++ b/stellarium_emul/main.c @@ -0,0 +1,376 @@ +/* + * main.c + * + * Copyright 2014 Edward V. Emelianov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// for pthread_kill +//#define _XOPEN_SOURCE 666 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usefull_macros.h" +#include "cmdlnopts.h" +#include "emulation.h" +#include "telescope.h" + +// daemon.c +extern void check4running(char *self, char *pidfilename, void (*iffound)(pid_t pid)); + +// Max amount of connections +#define BACKLOG (1) +#define BUFLEN (1024) +// pause for incoming message waiting (out coordinates sent after that timeout) +#define SOCK_TMOUT (1) + +static uint8_t buff[BUFLEN+1]; +// global parameters +static glob_pars *GP = NULL; + +//glob_pars *Global_parameters = NULL; + +static volatile int global_quit = 0; +// quit by signal +void signals(int sig){ + signal(sig, SIG_IGN); + restore_console(); + //restore_tty(); + DBG("Get signal %d, quit.\n", sig); + global_quit = 1; + sleep(1); + putlog("Exit with status %d", sig); + exit(sig); +} + +// search a first word after needle without spaces +char* stringscan(char *str, char *needle){ + char *a, *e; + char *end = str + strlen(str); + a = strstr(str, needle); + if(!a) return NULL; + a += strlen(needle); + while (a < end && (*a == ' ' || *a == '\r' || *a == '\t')) a++; + if(a >= end) return NULL; + e = strchr(a, ' '); + if(e) *e = 0; + return a; +} + +/** + * Send data to user + * @param data - data to send + * @param dlen - data length + * @param sockfd - socket fd for sending data + * @return 0 if failed + */ +int send_data(uint8_t *data, size_t dlen, int sockfd){ + size_t sent = write(sockfd, data, dlen); + if(sent != dlen){ + WARN("write()"); + return 0; + } + return 1; +} + +//read: 0x14 0x0 0x0 0x0 0x5b 0x5a 0x2e 0xc6 0x8c 0x23 0x5 0x0 0x23 0x9 0xe5 0xaf 0x23 0x2e 0x34 0xed +// command: goto 16h29 24.45 -26d25 55.62 +/* + LITTLE-ENDIAN!!! + from client: +LENGTH (2 bytes, integer): length of the message +TYPE (2 bytes, integer): 0 +TIME (8 bytes, integer): current time on the server computer in microseconds + since 1970.01.01 UT. Currently unused. +RA (4 bytes, unsigned integer): right ascension of the telescope (J2000) + a value of 0x100000000 = 0x0 means 24h=0h, + a value of 0x80000000 means 12h +DEC (4 bytes, signed integer): declination of the telescope (J2000) + a value of -0x40000000 means -90degrees, + a value of 0x0 means 0degrees, + a value of 0x40000000 means 90degrees + +to client: +LENGTH (2 bytes, integer): length of the message +TYPE (2 bytes, integer): 0 +TIME (8 bytes, integer): current time on the server computer in microseconds + since 1970.01.01 UT. Currently unused. +RA (4 bytes, unsigned integer): right ascension of the telescope (J2000) + a value of 0x100000000 = 0x0 means 24h=0h, + a value of 0x80000000 means 12h +DEC (4 bytes, signed integer): declination of the telescope (J2000) + a value of -0x40000000 means -90degrees, + a value of 0x0 means 0degrees, + a value of 0x40000000 means 90degrees +STATUS (4 bytes, signed integer): status of the telescope, currently unused. + status=0 means ok, status<0 means some error +*/ + +#define DEG2DEC(degr) ((int32_t)(degr / 90. * ((double)0x40000000))) +#define HRS2RA(hrs) ((uint32_t)(hrs / 12. * ((double)0x80000000))) +#define DEC2DEG(i32) (((double)i32)*90./((double)0x40000000)) +#define RA2HRS(u32) (((double)u32)*12. /((double)0x80000000)) + +typedef struct __attribute__((__packed__)){ + uint16_t len; + uint16_t type; + uint64_t time; + uint32_t ra; + int32_t dec; +} indata; + +typedef struct __attribute__((__packed__)){ + uint16_t len; + uint16_t type; + uint64_t time; + uint32_t ra; + int32_t dec; + int32_t status; +} outdata; + +double glob_RA, glob_Decl; // global variables: RA & declination (hours & degrees) + +/** + * send input RA/Decl (j2000!) coordinates to tel + * ra in hours (0..24), decl in degrees (-360..360) + * @return 1 if all OK + */ +int setCoords(double ra, double dec){ + DBG("Set RA/Decl to %g, %g", ra, dec); + putlog("Set RA/Decl to %g, %g", ra, dec); + int (*pointfunction)(double, double) = point_telescope; + if(GP->emulation) pointfunction = point_emulation; + return pointfunction(ra, dec); +} + +int proc_data(uint8_t *data, ssize_t len){ + FNAME(); + if(len != sizeof(indata)){ + WARN("Bad data size: got %zd instead of %zd!", len, sizeof(indata)); + return 0; + } + indata *dat = (indata*)data; + uint16_t L, T; + //uint64_t tim; + uint32_t ra; + int32_t dec; +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + L = le16toh(dat->len); T = le16toh(dat->type); + //tim = le64toh(dat->time); + ra = le32toh(dat->ra); + dec = (int32_t)le32toh((uint32_t)dat->dec); +#else + L = dat->len; T = dat->type; + //tim = dat->time; + ra = dat->ra; dec = dat->dec; +#endif + WARN("got message with len %u & type %u", L, T); + double tagRA = RA2HRS(ra), tagDec = DEC2DEG(dec); + WARN("RA: %u (%g), DEC: %d (%g)", ra, tagRA, + dec, tagDec); + if(!setCoords(tagRA, tagDec)) return 0; + return 1; +} + +/** + * main socket service procedure + */ +void handle_socket(int sock){ + FNAME(); + if(global_quit) return; + ssize_t rd; + outdata dout; + dout.len = htole16(sizeof(outdata)); + dout.type = 0; + dout.status = 0; + int (*getcoords)(double*, double*) = get_telescope_coords; + if(GP->emulation) getcoords = get_emul_coords; + while(!global_quit){ + // get coordinates + if(!getcoords(&glob_RA, &glob_Decl)){ + WARNX("Error: can't get coordinates"); + continue; + } + DBG("got : %g/%g", glob_RA, glob_Decl); + dout.ra = htole32(HRS2RA(glob_RA)); + dout.dec = (int32_t)htole32(DEG2DEC(glob_Decl)); + if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break; + DBG("sent ra = %g, dec = %g", RA2HRS(dout.ra), DEC2DEG(dout.dec)); + fd_set readfds; + struct timeval timeout; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + timeout.tv_sec = SOCK_TMOUT; // wait not more than SOCK_TMOUT second + timeout.tv_usec = 0; + int sel = select(sock + 1 , &readfds , NULL , NULL , &timeout); + if(sel < 0){ + if(errno != EINTR) + WARN("select()"); + continue; + } + if(!(FD_ISSET(sock, &readfds))) continue; + // fill incoming buffer + rd = read(sock, buff, BUFLEN); + DBG("read %zd", rd); + if(rd <= 0){ // error or disconnect + DBG("Nothing to read from fd %d (ret: %zd)", sock, rd); + break; + } + /************************************** + * DO SOMETHING WITH DATA * + **************************************/ + if(!proc_data(buff, rd)) dout.status = -1; + else dout.status = 0; + } + close(sock); +} + +static inline void main_proc(){ + int sock; + int reuseaddr = 1; + // connect to telescope + if(!GP->emulation){ + if(!connect_telescope(GP->device)){ + ERRX(_("Can't connect to telescope device")); + } + } + // open socket + struct addrinfo hints, *res, *p; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + DBG("try to open port %s", GP->port); + if(getaddrinfo(NULL, GP->port, &hints, &res) != 0){ + ERR("getaddrinfo"); + } + struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr; + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN); + // loop through all the results and bind to the first we can + for(p = res; p != NULL; p = p->ai_next){ + if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){ + WARN("socket"); + continue; + } + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + ERR("setsockopt"); + } + if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){ + close(sock); + WARN("bind"); + continue; + } + break; // if we get here, we must have connected successfully + } + // Listen + if(listen(sock, BACKLOG) == -1){ + putlog("listen() error"); + ERR("listen"); + } + DBG("listen at %s", GP->port); + putlog("listen at %s", GP->port); + //freeaddrinfo(res); + // Main loop + while(!global_quit){ + socklen_t size = sizeof(struct sockaddr_in); + struct sockaddr_in myaddr; + int newsock; + newsock = accept(sock, (struct sockaddr*)&myaddr, &size); + if(newsock <= 0){ + WARN("accept()"); + continue; + } + struct sockaddr_in peer; + socklen_t peer_len = sizeof(peer); + if (getpeername(newsock, &peer, &peer_len) == -1) { + WARN("getpeername()"); + close(newsock); + continue; + } + char *peerIP = inet_ntoa(peer.sin_addr); + putlog("Got connection from %s", peerIP); + DBG("Peer's IP address is: %s\n", peerIP); + /*if(strcmp(peerIP, ACCEPT_IP) && strcmp(peerIP, "127.0.0.1")){ + WARNX("Wrong IP"); + close(newsock); + continue; + }*/ + handle_socket(newsock); + } + close(sock); +} + +int main(int argc, char **argv){ + char *self = strdup(argv[0]); + GP = parse_args(argc, argv); + check4running(self, GP->pidfile, NULL); + if(GP->logfile) openlogfile(GP->logfile); + initial_setup(); + + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + + printf(_("Start socket\n")); + +#ifndef EBUG // daemonize only in release mode + if(daemon(1, 0)){ + putlog("Err: daemon()"); + ERR("daemon()"); + } +#endif // EBUG + + while(1){ + pid_t childpid = fork(); + if(childpid < 0){ + putlog("fork() error"); + ERR("ERROR on fork"); + } + if(childpid){ + putlog("Created child with PID %d\n", childpid); + DBG("Created child with PID %d\n", childpid); + wait(NULL); + putlog("Child %d died\n", childpid); + DBG("Child %d died\n", childpid); + }else{ + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + main_proc(); + return 0; + } + } + + return 0; +} diff --git a/stellarium_emul/main.h b/stellarium_emul/main.h new file mode 100644 index 0000000..ec6d9c1 --- /dev/null +++ b/stellarium_emul/main.h @@ -0,0 +1,42 @@ +/* + * main.h + * + * Copyright 2014 Edward V. Emelianov + * + * 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. + */ +#pragma once +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "usefull_macros.h" + +// global parameters +extern glob_pars *Global_parameters; + +#endif // __MAIN_H__ diff --git a/stellarium_emul/parseargs.c b/stellarium_emul/parseargs.c new file mode 100644 index 0000000..b235752 --- /dev/null +++ b/stellarium_emul/parseargs.c @@ -0,0 +1,497 @@ +/* geany_encoding=koi8-r + * parseargs.c - parsing command line arguments & print help + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ + +#include // printf +#include // getopt_long +#include // calloc, exit, strtoll +#include // assert +#include // strdup, strchr, strlen +#include // strcasecmp +#include // INT_MAX & so on +#include // gettext +#include // isalpha +#include "parseargs.h" +#include "usefull_macros.h" + +char *helpstring = "%s\n"; + +/** + * Change standard help header + * MAY consist ONE "%s" for progname + * @param str (i) - new format + */ +void change_helpstring(char *s){ + int pcount = 0, scount = 0; + char *str = s; + // check `helpstring` and set it to default in case of error + for(; pcount < 2; str += 2){ + if(!(str = strchr(str, '%'))) break; + if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%" + else{ + str += 2; // pass next '%' + continue; + } + if(str[1] == 's') scount++; // increment "%s" counter + }; + if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong + /// "Неправильный формат строки помощи" + ERRX(_("Wrong helpstring!")); + } + helpstring = s; +} + +/** + * Carefull atoll/atoi + * @param num (o) - returning value (or NULL if you wish only check number) - allocated by user + * @param str (i) - string with number must not be NULL + * @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more) + * @return TRUE if conversion sone without errors, FALSE otherwise + */ +static bool myatoll(void *num, char *str, argtype t){ + long long tmp, *llptr; + int *iptr; + char *endptr; + assert(str); + assert(num); + tmp = strtoll(str, &endptr, 0); + if(endptr == str || *str == '\0' || *endptr != '\0') + return FALSE; + switch(t){ + case arg_longlong: + llptr = (long long*) num; + *llptr = tmp; + break; + case arg_int: + default: + if(tmp < INT_MIN || tmp > INT_MAX){ + /// "Целое вне допустимого диапазона" + WARNX(_("Integer out of range")); + return FALSE; + } + iptr = (int*)num; + *iptr = (int)tmp; + } + return TRUE; +} + +// the same as myatoll but for double +// There's no NAN & INF checking here (what if they would be needed?) +static bool myatod(void *num, const char *str, argtype t){ + double tmp, *dptr; + float *fptr; + char *endptr; + assert(str); + tmp = strtod(str, &endptr); + if(endptr == str || *str == '\0' || *endptr != '\0') + return FALSE; + switch(t){ + case arg_double: + dptr = (double *) num; + *dptr = tmp; + break; + case arg_float: + default: + fptr = (float *) num; + *fptr = (float)tmp; + break; + } + return TRUE; +} + +/** + * Get index of current option in array options + * @param opt (i) - returning val of getopt_long + * @param options (i) - array of options + * @return index in array + */ +static int get_optind(int opt, myoption *options){ + int oind; + myoption *opts = options; + assert(opts); + for(oind = 0; opts->name && opts->val != opt; oind++, opts++); + if(!opts->name || opts->val != opt) // no such parameter + showhelp(-1, options); + return oind; +} + +/** + * reallocate new value in array of multiple repeating arguments + * @arg paptr - address of pointer to array (**void) + * @arg type - its type (for realloc) + * @return pointer to new (next) value + */ +void *get_aptr(void *paptr, argtype type){ + int i = 1; + void **aptr = *((void***)paptr); + if(aptr){ // there's something in array + void **p = aptr; + while(*p++) ++i; + } + size_t sz = 0; + switch(type){ + default: + case arg_none: + /// "Не могу использовать несколько параметров без аргументов!" + ERRX("Can't use multiple args with arg_none!"); + break; + case arg_int: + sz = sizeof(int); + break; + case arg_longlong: + sz = sizeof(long long); + break; + case arg_double: + sz = sizeof(double); + break; + case arg_float: + sz = sizeof(float); + break; + case arg_string: + sz = 0; + break; + /* case arg_function: + sz = sizeof(argfn *); + break;*/ + } + aptr = realloc(aptr, (i + 1) * sizeof(void*)); + *((void***)paptr) = aptr; + aptr[i] = NULL; + if(sz){ + aptr[i - 1] = malloc(sz); + }else + aptr[i - 1] = &aptr[i - 1]; + return aptr[i - 1]; +} + + +/** + * Parse command line arguments + * ! If arg is string, then value will be strdup'ed! + * + * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` + * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` + * BE CAREFUL! if you wanna use full argc & argv, save their original values before + * calling this function + * @param options (i) - array of `myoption` for arguments parcing + * + * @exit: in case of error this function show help & make `exit(-1)` + */ +void parseargs(int *argc, char ***argv, myoption *options){ + char *short_options, *soptr; + struct option *long_options, *loptr; + size_t optsize, i; + myoption *opts = options; + // check whether there is at least one options + assert(opts); + assert(opts[0].name); + // first we count how much values are in opts + for(optsize = 0; opts->name; optsize++, opts++); + // now we can allocate memory + short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts + long_options = calloc(optsize + 1, sizeof(struct option)); + opts = options; loptr = long_options; soptr = short_options; + // in debug mode check the parameters are not repeated +#ifdef EBUG + char **longlist = MALLOC(char*, optsize); + char *shortlist = MALLOC(char, optsize); +#endif + // fill short/long parameters and make a simple checking + for(i = 0; i < optsize; i++, loptr++, opts++){ + // check + assert(opts->name); // check name +#ifdef EBUG + longlist[i] = strdup(opts->name); +#endif + if(opts->has_arg){ + assert(opts->type != arg_none); // check error with arg type + assert(opts->argptr); // check pointer + } + if(opts->type != arg_none) // if there is a flag without arg, check its pointer + assert(opts->argptr); + // fill long_options + // don't do memcmp: what if there would be different alignment? + loptr->name = opts->name; + loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; + loptr->flag = opts->flag; + loptr->val = opts->val; + // fill short options if they are: + if(!opts->flag && opts->val){ +#ifdef EBUG + shortlist[i] = (char) opts->val; +#endif + *soptr++ = opts->val; + if(loptr->has_arg) // add ':' if option has required argument + *soptr++ = ':'; + if(loptr->has_arg == 2) // add '::' if option has optional argument + *soptr++ = ':'; + } + } + // sort all lists & check for repeating +#ifdef EBUG + int cmpstringp(const void *p1, const void *p2){ + return strcmp(* (char * const *) p1, * (char * const *) p2); + } + int cmpcharp(const void *p1, const void *p2){ + return (int)(*(char * const)p1 - *(char *const)p2); + } + qsort(longlist, optsize, sizeof(char *), cmpstringp); + qsort(shortlist,optsize, sizeof(char), cmpcharp); + char *prevl = longlist[0], prevshrt = shortlist[0]; + for(i = 1; i < optsize; ++i){ + if(longlist[i]){ + if(prevl){ + if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl); + } + prevl = longlist[i]; + } + if(shortlist[i]){ + if(prevshrt){ + if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt); + } + prevshrt = shortlist[i]; + } + } +#endif + // now we have both long_options & short_options and can parse `getopt_long` + while(1){ + int opt; + int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[] + if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break; + if(opt == '?'){ + opt = optopt; + optind = get_optind(opt, options); + if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR) + showhelp(optind, options); // need argument + } + else{ + if(opt == 0 || oindex > 0) optind = oindex; + else optind = get_optind(opt, options); + } + opts = &options[optind]; + // if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag + // now check option + if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) + if(!optarg) showhelp(optind, options); // need argument + void *aptr; + if(opts->has_arg == MULT_PAR){ + aptr = get_aptr(opts->argptr, opts->type); + }else + aptr = opts->argptr; + bool result = TRUE; + // even if there is no argument, but argptr != NULL, think that optarg = "1" + if(!optarg) optarg = "1"; + switch(opts->type){ + default: + case arg_none: + if(opts->argptr) *((int*)aptr) += 1; // increment value + break; + case arg_int: + result = myatoll(aptr, optarg, arg_int); + break; + case arg_longlong: + result = myatoll(aptr, optarg, arg_longlong); + break; + case arg_double: + result = myatod(aptr, optarg, arg_double); + break; + case arg_float: + result = myatod(aptr, optarg, arg_float); + break; + case arg_string: + result = (*((void**)aptr) = (void*)strdup(optarg)); + break; + case arg_function: + result = ((argfn)aptr)(optarg); + break; + } + if(!result){ + showhelp(optind, options); + } + } + *argc -= optind; + *argv += optind; +} + +/** + * compare function for qsort + * first - sort by short options; second - sort arguments without sort opts (by long options) + */ +static int argsort(const void *a1, const void *a2){ + const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2; + const char *l1 = o1->name, *l2 = o2->name; + int s1 = o1->val, s2 = o2->val; + int *f1 = o1->flag, *f2 = o2->flag; + // check if both options has short arg + if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg + return (s1 - s2); + }else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long + return strcmp(l1, l2); + }else{ // only one have short arg -- return it + if(f2 || !s2) return -1; // a1 have short - it is 'lesser' + else return 1; + } +} + +/** + * Show help information based on myoption->help values + * @param oindex (i) - if non-negative, show only help by myoption[oindex].help + * @param options (i) - array of `myoption` + * + * @exit: run `exit(-1)` !!! + */ +void showhelp(int oindex, myoption *options){ + int max_opt_len = 0; // max len of options substring - for right indentation + const int bufsz = 255; + char buf[bufsz+1]; + myoption *opts = options; + assert(opts); + assert(opts[0].name); // check whether there is at least one options + if(oindex > -1){ // print only one message + opts = &options[oindex]; + printf(" "); + if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val); + printf("--%s", opts->name); + if(opts->has_arg == 1) printf("=arg"); + else if(opts->has_arg == 2) printf("[=arg]"); + printf(" %s\n", _(opts->help)); + exit(-1); + } + // header, by default is just "progname\n" + printf("\n"); + if(strstr(helpstring, "%s")) // print progname + printf(helpstring, __progname); + else // only text + printf("%s", helpstring); + printf("\n"); + // count max_opt_len + do{ + int L = strlen(opts->name); + if(max_opt_len < L) max_opt_len = L; + }while((++opts)->name); + max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols + opts = options; + // count amount of options + int N; for(N = 0; opts->name; ++N, ++opts); + if(N == 0) exit(-2); + // Now print all help (sorted) + opts = options; + qsort(opts, N, sizeof(myoption), argsort); + do{ + int p = sprintf(buf, " "); // a little indent + if(!opts->flag && opts->val) // .val is short argument + p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val); + p += snprintf(buf+p, bufsz-p, "--%s", opts->name); + if(opts->has_arg == 1) // required argument + p += snprintf(buf+p, bufsz-p, "=arg"); + else if(opts->has_arg == 2) // optional argument + p += snprintf(buf+p, bufsz-p, "[=arg]"); + assert(p < max_opt_len); // there would be magic if p >= max_opt_len + printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after + ++opts; + }while(--N); + printf("\n\n"); + exit(-1); +} + +/** + * get suboptions from parameter string + * @param str - parameter string + * @param opt - pointer to suboptions structure + * @return TRUE if all OK + */ +bool get_suboption(char *str, mysuboption *opt){ + int findsubopt(char *par, mysuboption *so){ + int idx = 0; + if(!par) return -1; + while(so[idx].name){ + if(strcasecmp(par, so[idx].name) == 0) return idx; + ++idx; + } + return -1; // badarg + } + bool opt_setarg(mysuboption *so, int idx, char *val){ + mysuboption *soptr = &so[idx]; + bool result = FALSE; + void *aptr = soptr->argptr; + switch(soptr->type){ + default: + case arg_none: + if(soptr->argptr) *((int*)aptr) += 1; // increment value + result = TRUE; + break; + case arg_int: + result = myatoll(aptr, val, arg_int); + break; + case arg_longlong: + result = myatoll(aptr, val, arg_longlong); + break; + case arg_double: + result = myatod(aptr, val, arg_double); + break; + case arg_float: + result = myatod(aptr, val, arg_float); + break; + case arg_string: + result = (*((void**)aptr) = (void*)strdup(val)); + break; + case arg_function: + result = ((argfn)aptr)(val); + break; + } + return result; + } + char *tok; + bool ret = FALSE; + char *tmpbuf; + tok = strtok_r(str, ":,", &tmpbuf); + do{ + char *val = strchr(tok, '='); + int noarg = 0; + if(val == NULL){ // no args + val = "1"; + noarg = 1; + }else{ + *val++ = '\0'; + if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after = + val = "1"; noarg = 1; + } + } + int idx = findsubopt(tok, opt); + if(idx < 0){ + /// "Неправильный параметр: %s" + WARNX(_("Wrong parameter: %s"), tok); + goto returning; + } + if(noarg && opt[idx].has_arg == NEED_ARG){ + /// "%s: необходим аргумент!" + WARNX(_("%s: argument needed!"), tok); + goto returning; + } + if(!opt_setarg(opt, idx, val)){ + /// "Неправильный аргумент \"%s\" параметра \"%s\"" + WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok); + goto returning; + } + }while((tok = strtok_r(NULL, ":,", &tmpbuf))); + ret = TRUE; +returning: + return ret; +} diff --git a/stellarium_emul/parseargs.h b/stellarium_emul/parseargs.h new file mode 100644 index 0000000..537fc5b --- /dev/null +++ b/stellarium_emul/parseargs.h @@ -0,0 +1,124 @@ +/* geany_encoding=koi8-r + * parseargs.h - headers for parsing command line arguments + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ +#pragma once +#ifndef __PARSEARGS_H__ +#define __PARSEARGS_H__ + +#include // bool +#include + +#ifndef TRUE + #define TRUE true +#endif + +#ifndef FALSE + #define FALSE false +#endif + +// macro for argptr +#define APTR(x) ((void*)x) + +// if argptr is a function: +typedef bool(*argfn)(void *arg); + +/* + * type of getopt's argument + * WARNING! + * My function change value of flags by pointer, so if you want to use another type + * make a latter conversion, example: + * char charg; + * int iarg; + * myoption opts[] = { + * {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option}; + * ..(parse args).. + * charg = (char) iarg; + */ +typedef enum { + arg_none = 0, // no arg + arg_int, // integer + arg_longlong, // long long + arg_double, // double + arg_float, // float + arg_string, // char * + arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)` +} argtype; + +/* + * Structure for getopt_long & help + * BE CAREFUL: .argptr is pointer to data or pointer to function, + * conversion depends on .type + * + * ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext, + * but you can redefine it before `#include "parseargs.h"` + * + * if arg is string, then value wil be strdup'ed like that: + * char *str; + * myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option}; + * *(opts[1].str) = strdup(optarg); + * in other cases argptr should be address of some variable (or pointer to allocated memory) + * + * NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr + * + * !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!! + * + */ +typedef enum{ + NO_ARGS = 0, // first three are the same as in getopt_long + NEED_ARG = 1, + OPT_ARG = 2, + MULT_PAR +} hasarg; + +typedef struct{ + // these are from struct option: + const char *name; // long option's name + hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array) + int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0) + int val; // short opt name (if flag == NULL) or flag's value + // and these are mine: + argtype type; // type of argument + void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)` + const char *help; // help string which would be shown in function `showhelp` or NULL +} myoption; + +/* + * Suboptions structure, almost the same like myoption + * used in parse_subopts() + */ +typedef struct{ + const char *name; + hasarg has_arg; + argtype type; + void *argptr; +} mysuboption; + +// last string of array (all zeros) +#define end_option {0,0,0,0,0,0,0} +#define end_suboption {0,0,0,0} + +extern const char *__progname; + +void showhelp(int oindex, myoption *options); +void parseargs(int *argc, char ***argv, myoption *options); +void change_helpstring(char *s); +bool get_suboption(char *str, mysuboption *opt); + +#endif // __PARSEARGS_H__ diff --git a/stellarium_emul/telescope.c b/stellarium_emul/telescope.c new file mode 100644 index 0000000..64b9903 --- /dev/null +++ b/stellarium_emul/telescope.c @@ -0,0 +1,66 @@ +/* + * geany_encoding=koi8-r + * telescope.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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. + * + */ +#include "telescope.h" +#include "usefull_macros.h" + +/** + * connect telescope device + * @param dev (i) - device name to connect + * @return 1 if all OK + */ +int connect_telescope(char *dev){ + DBG("Connect to device %s\n", dev); + putlog("Connect to device %s\n", dev); + /* + * Main code here + */ + return 0; +} + +/** + * send coordinates to telescope + * @param ra - right ascention (hours) + * @param decl - declination (degrees) + * @return 1 if all OK + */ +int point_telescope(double ra, double decl){ + DBG("Send ra=%g, decl=%g", ra, decl); + putlog("Send ra=%g, decl=%g", ra, decl); + /* + * Main code here + */ + return 0; +} + +/** + * get coordinates + * @return 1 if all OK + */ +int get_telescope_coords(double *ra, double *decl){ + (void)ra; + (void)decl; + /* + * Main code here + */ + return 0; +} diff --git a/stellarium_emul/telescope.h b/stellarium_emul/telescope.h new file mode 100644 index 0000000..836d224 --- /dev/null +++ b/stellarium_emul/telescope.h @@ -0,0 +1,31 @@ +/* + * geany_encoding=koi8-r + * telescope.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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. + * + */ +#pragma once +#ifndef __TELESCOPE_H__ +#define __TELESCOPE_H__ + +int connect_telescope(char *dev); +int point_telescope(double ra, double decl); +int get_telescope_coords(double *ra, double *decl); + +#endif // __TELESCOPE_H__ diff --git a/stellarium_emul/usefull_macros.c b/stellarium_emul/usefull_macros.c new file mode 100644 index 0000000..de3e03e --- /dev/null +++ b/stellarium_emul/usefull_macros.c @@ -0,0 +1,432 @@ +/* geany_encoding=koi8-r + * usefull_macros.h - a set of usefull functions: memory, color etc + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ + +#include "usefull_macros.h" +#include // PATH_MAX + +/** + * function for different purposes that need to know time intervals + * @return double value: time in seconds + */ +double dtime(){ + double t; + struct timeval tv; + gettimeofday(&tv, NULL); + t = tv.tv_sec + ((double)tv.tv_usec)/1e6; + return t; +} + +/******************************************************************************\ + * Coloured terminal +\******************************************************************************/ +int globErr = 0; // errno for WARN/ERR + +// pointers to coloured output printf +int (*red)(const char *fmt, ...); +int (*green)(const char *fmt, ...); +int (*_WARN)(const char *fmt, ...); + +/* + * format red / green messages + * name: r_pr_, g_pr_ + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int r_pr_(const char *fmt, ...){ + va_list ar; int i; + printf(RED); + va_start(ar, fmt); + i = vprintf(fmt, ar); + va_end(ar); + printf(OLDCOLOR); + return i; +} +int g_pr_(const char *fmt, ...){ + va_list ar; int i; + printf(GREEN); + va_start(ar, fmt); + i = vprintf(fmt, ar); + va_end(ar); + printf(OLDCOLOR); + return i; +} +/* + * print red error/warning messages (if output is a tty) + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int r_WARN(const char *fmt, ...){ + va_list ar; int i = 1; + fprintf(stderr, RED); + va_start(ar, fmt); + if(globErr){ + errno = globErr; + vwarn(fmt, ar); + errno = 0; + }else + i = vfprintf(stderr, fmt, ar); + va_end(ar); + i++; + fprintf(stderr, OLDCOLOR "\n"); + return i; +} + +static const char stars[] = "****************************************"; +/* + * notty variants of coloured printf + * name: s_WARN, r_pr_notty + * @param fmt ... - printf-like format + * @return number of printed symbols + */ +int s_WARN(const char *fmt, ...){ + va_list ar; int i; + i = fprintf(stderr, "\n%s\n", stars); + va_start(ar, fmt); + if(globErr){ + errno = globErr; + vwarn(fmt, ar); + errno = 0; + }else + i = +vfprintf(stderr, fmt, ar); + va_end(ar); + i += fprintf(stderr, "\n%s\n", stars); + i += fprintf(stderr, "\n"); + return i; +} +int r_pr_notty(const char *fmt, ...){ + va_list ar; int i; + i = printf("\n%s\n", stars); + va_start(ar, fmt); + i += vprintf(fmt, ar); + va_end(ar); + i += printf("\n%s\n", stars); + return i; +} + +/** + * Run this function in the beginning of main() to setup locale & coloured output + */ +void initial_setup(){ + // setup coloured output + if(isatty(STDOUT_FILENO)){ // make color output in tty + red = r_pr_; green = g_pr_; + }else{ // no colors in case of pipe + red = r_pr_notty; green = printf; + } + if(isatty(STDERR_FILENO)) _WARN = r_WARN; + else _WARN = s_WARN; + // Setup locale + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); +#if defined GETTEXT_PACKAGE && defined LOCALEDIR + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + textdomain(GETTEXT_PACKAGE); +#endif +} + +/******************************************************************************\ + * Memory +\******************************************************************************/ +/* + * safe memory allocation for macro ALLOC + * @param N - number of elements to allocate + * @param S - size of single element (typically sizeof) + * @return pointer to allocated memory area + */ +void *my_alloc(size_t N, size_t S){ + void *p = calloc(N, S); + if(!p) ERR("malloc"); + //assert(p); + return p; +} + +/** + * Mmap file to a memory area + * + * @param filename (i) - name of file to mmap + * @return stuct with mmap'ed file or die + */ +mmapbuf *My_mmap(char *filename){ + int fd; + char *ptr; + size_t Mlen; + struct stat statbuf; + /// "Не задано имя файла!" + if(!filename){ + WARNX(_("No filename given!")); + return NULL; + } + if((fd = open(filename, O_RDONLY)) < 0){ + /// "Не могу открыть %s для чтения" + WARN(_("Can't open %s for reading"), filename); + return NULL; + } + if(fstat (fd, &statbuf) < 0){ + /// "Не могу выполнить stat %s" + WARN(_("Can't stat %s"), filename); + close(fd); + return NULL; + } + Mlen = statbuf.st_size; + if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){ + /// "Ошибка mmap" + WARN(_("Mmap error for input")); + close(fd); + return NULL; + } + /// "Не могу закрыть mmap'нутый файл" + if(close(fd)) WARN(_("Can't close mmap'ed file")); + mmapbuf *ret = MALLOC(mmapbuf, 1); + ret->data = ptr; + ret->len = Mlen; + return ret; +} + +void My_munmap(mmapbuf *b){ + if(munmap(b->data, b->len)){ + /// "Не могу munmap" + ERR(_("Can't munmap")); + } + FREE(b); +} + + +/******************************************************************************\ + * Terminal in no-echo mode +\******************************************************************************/ +static struct termios oldt, newt; // terminal flags +static int console_changed = 0; +// run on exit: +void restore_console(){ + if(console_changed) + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state + console_changed = 0; +} + +// initial setup: +void setup_con(){ + if(console_changed) return; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){ + /// "Не могу настроить консоль" + WARN(_("Can't setup console")); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + signals(0); //quit? + } + console_changed = 1; +} + +/** + * Read character from console without echo + * @return char readed + */ +int read_console(){ + int rb; + struct timeval tv; + int retval; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + tv.tv_sec = 0; tv.tv_usec = 10000; + retval = select(1, &rfds, NULL, NULL, &tv); + if(!retval) rb = 0; + else { + if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar(); + else rb = 0; + } + return rb; +} + +/** + * getchar() without echo + * wait until at least one character pressed + * @return character readed + */ +int mygetchar(){ // getchar() without need of pressing ENTER + int ret; + do ret = read_console(); + while(ret == 0); + return ret; +} + + +/******************************************************************************\ + * TTY with select() +\******************************************************************************/ +static struct termio oldtty, tty; // TTY flags +static int comfd = -1; // TTY fd + +// run on exit: +void restore_tty(){ + if(comfd == -1) return; + ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state + close(comfd); + comfd = -1; +} + +#ifndef BAUD_RATE +#define BAUD_RATE B9600 +#endif +// init: (speed = B9600 etc) +void tty_init(char *comdev, tcflag_t speed){ + if(comfd == -1){ // not opened + if(!comdev){ + WARNX("comdev == NULL"); + signals(11); + } + DBG("Open port..."); + do{ + comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK); + }while (comfd == -1 && errno == EINTR); + if(comfd < 0){ + WARN(_("Can't open port %s"),comdev); + signals(2); + } + /* DBG("OK\nGet current settings..."); + if(ioctl(comfd, TCGETA, &oldtty) < 0){ // Get settings + /// "Не могу получить настройки" + WARN(_("Can't get settings")); + signals(2); + }*/ + DBG("Make exclusive"); + // make exclusive open + if(ioctl(comfd, TIOCEXCL)){ + WARN(_("Can't do exclusive open")); + close(comfd); + signals(2); + } + } + tty = oldtty; + tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG) + tty.c_oflag = 0; + tty.c_cflag = speed|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl + tty.c_cc[VMIN] = 0; // non-canonical mode + tty.c_cc[VTIME] = 5; + if(ioctl(comfd, TCSETA, &tty) < 0){ + /// "Не могу установить настройки" + WARN(_("Can't set settings")); + signals(0); + } + DBG("OK"); +} +/** + * Read data from TTY + * @param buff (o) - buffer for data read + * @param length - buffer len + * @return amount of readed bytes + */ +size_t read_tty(uint8_t *buff, size_t length){ + if(comfd < 0) return 0; + ssize_t L = 0; + fd_set rfds; + struct timeval tv; + int retval; + FD_ZERO(&rfds); + FD_SET(comfd, &rfds); + tv.tv_sec = 0; tv.tv_usec = 50000; // wait for 50ms + retval = select(comfd + 1, &rfds, NULL, NULL, &tv); + if (!retval) return 0; + if(FD_ISSET(comfd, &rfds)){ + if((L = read(comfd, buff, length)) < 1) return 0; + } + return (size_t)L; +} + +int write_tty(const uint8_t *buff, size_t length){ + if(comfd < 0) return 1; + ssize_t L = write(comfd, buff, length); + if((size_t)L != length){ + /// "Ошибка записи!" + WARN("Write error!"); + return 1; + } + return 0; +} + + +/** + * Safely convert data from string to double + * + * @param num (o) - double number read from string + * @param str (i) - input string + * @return 1 if success, 0 if fails + */ +int str2double(double *num, const char *str){ + double res; + char *endptr; + if(!str) return 0; + res = strtod(str, &endptr); + if(endptr == str || *str == '\0' || *endptr != '\0'){ + /// "Неправильный формат числа double!" + WARNX("Wrong double number format!"); + return FALSE; + } + if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number + return TRUE; +} + +FILE *Flog = NULL; // log file descriptor +char *logname = NULL; +time_t log_open_time = 0; +/** + * Try to open log file + * if failed show warning message + */ +void openlogfile(char *name){ + if(!name){ + WARNX(_("Need filename")); + return; + } + green(_("Try to open log file %s in append mode\n"), name); + if(!(Flog = fopen(name, "a"))){ + WARN(_("Can't open log file")); + return; + } + log_open_time = time(NULL); + logname = name; +} + +/** + * Save message to log file, rotate logs every 24 hours + */ +int putlog(const char *fmt, ...){ + if(!Flog) return 0; + time_t t_now = time(NULL); + if(t_now - log_open_time > 86400){ // rotate log + fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now)); + fclose(Flog); + char newname[PATH_MAX]; + snprintf(newname, PATH_MAX, "%s.old", logname); + if(rename(logname, newname)) WARN("rename()"); + openlogfile(logname); + if(!Flog) return 0; + } + int i = fprintf(Flog, "\n\t\t%s", ctime(&t_now)); + va_list ar; + va_start(ar, fmt); + i = vfprintf(Flog, fmt, ar); + va_end(ar); + fprintf(Flog, "\n"); + fflush(Flog); + return i; +} diff --git a/stellarium_emul/usefull_macros.h b/stellarium_emul/usefull_macros.h new file mode 100644 index 0000000..6a94ae0 --- /dev/null +++ b/stellarium_emul/usefull_macros.h @@ -0,0 +1,144 @@ +/* geany_encoding=koi8-r + * usefull_macros.h - a set of usefull macros: memory, color etc + * + * Copyright 2013 Edward V. Emelianoff + * + * 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. + */ + +#pragma once +#ifndef __USEFULL_MACROS_H__ +#define __USEFULL_MACROS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined GETTEXT_PACKAGE && defined LOCALEDIR +/* + * GETTEXT + */ +#include +#define _(String) gettext(String) +#define gettext_noop(String) String +#define N_(String) gettext_noop(String) +#else +#define _(String) (String) +#define N_(String) (String) +#endif +#include +#include +#include +#include +#include +#include +#include + + +// unused arguments with -Wall -Werror +#define _U_ __attribute__((__unused__)) + +/* + * Coloured messages output + */ +#define RED "\033[1;31;40m" +#define GREEN "\033[1;32;40m" +#define OLDCOLOR "\033[0;0;0m" + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +/* + * ERROR/WARNING messages + */ +extern int globErr; +extern void signals(int sig); +#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); signals(9);}while(0) +#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); signals(9);}while(0) +#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0) +#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0) + +/* + * print function name, debug messages + * debug mode, -DEBUG + */ +#ifdef EBUG + #define FNAME() do{ fprintf(stderr, OLDCOLOR); \ + fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__);} while(0) + #define DBG(...) do{ fprintf(stderr, OLDCOLOR); \ + fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n");} while(0) +#else + #define FNAME() do{}while(0) + #define DBG(...) do{}while(0) +#endif //EBUG + +/* + * Memory allocation + */ +#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type))) +#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type))) +#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0) + +#ifndef DBL_EPSILON +#define DBL_EPSILON (2.2204460492503131e-16) +#endif + +double dtime(); + +// functions for color output in tty & no-color in pipes +extern int (*red)(const char *fmt, ...); +extern int (*_WARN)(const char *fmt, ...); +extern int (*green)(const char *fmt, ...); +void * my_alloc(size_t N, size_t S); +void initial_setup(); + +// mmap file +typedef struct{ + char *data; + size_t len; +} mmapbuf; +mmapbuf *My_mmap(char *filename); +void My_munmap(mmapbuf *b); + +void restore_console(); +void setup_con(); +int read_console(); +int mygetchar(); + +void restore_tty(); +void tty_init(char *comdev, tcflag_t speed); +size_t read_tty(uint8_t *buff, size_t length); +int write_tty(const uint8_t *buff, size_t length); + +int str2double(double *num, const char *str); + +void openlogfile(char *name); +int putlog(const char *fmt, ...); + +#endif // __USEFULL_MACROS_H__ diff --git a/tmout.c b/tmout.c new file mode 100644 index 0000000..e1b55cb --- /dev/null +++ b/tmout.c @@ -0,0 +1,114 @@ +/* + * geany_encoding=koi8-r + * tmout.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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. + * + */ + +// define something else +#define WARN(...) do{fprintf(stderr, __VA_ARGS__);}while(0) +#define _(arg) arg + +// content of header file (tmout.h): +#include + +extern int verbose; +extern char indi[]; + +#define PRINT(...) do{if(verbose) printf(__VA_ARGS__);}while(0) + +#define WAIT_EVENT(evt, max_delay) do{int __ = 0; set_timeout(max_delay); \ + char *iptr = indi; PRINT(" "); while(!tmout && !(evt)){ \ + usleep(100000); if(!*(++iptr)) iptr = indi; if(++__%10==0) PRINT("\b. "); \ + PRINT("\b%c", *iptr);}; PRINT("\n");}while(0) + +void set_timeout(double delay); +extern volatile int tmout; + +// content of c file (tmout.c): + +#include +#include +#include +#include +#include + +char indi[] = "|/-\\"; +volatile int tmout = 0; + +void *tmout_thread(void *buf){ + int selfd = -1; + struct timeval *tv = (struct timeval *) buf; + errno = 0; + while(selfd < 0){ + selfd = select(0, NULL, NULL, NULL, tv); + if(selfd < 0 && errno != EINTR){ + WARN(_("Error while select()")); + tmout = 1; + return NULL; + } + } + tmout = 1; + return NULL; +} + +/** + * run thread with pause [delay] (in seconds), at its end set variable tmout + */ +void set_timeout(double delay){ + static int run = 0; + static pthread_t athread; + static struct timeval tv; // should be static to send this as argument of tmout_thread + if(delay < 0.){ + tmout = 1; + return; + } + if(run && (pthread_kill(athread, 0) != ESRCH)){ // another timeout process detected - kill it + pthread_cancel(athread); + pthread_join(athread, NULL); + } + tmout = 0; + run = 1; + tv.tv_sec = (time_t) delay; + tv.tv_usec = (suseconds_t)((delay-(double)tv.tv_sec)*1e6); + if(pthread_create(&athread, NULL, tmout_thread, (void*)&tv)){ + WARN(_("Can't create timeout thread!")); + tmout = 1; + return; + } +} + +// main.c +#include + +int verbose = 0; + +int main(int argc, char **argv){ + // check `verbose` + if(argc == 2 && strcmp(argv[1], "-v") == 0){ + printf("Will show indicator\n"); + verbose = 1; + } + setbuf(stdout, NULL); // without this string there will be no indicator! + printf("wait for 2.574 seconds\n"); + WAIT_EVENT(0, 2.574); + if(tmout) printf("Timeout\n"); + else printf("Never reached\n"); + return 0; +}