cmake_minimum_required(VERSION 3.20)
project(winlibedit LANGUAGES C)

set(LT_VERSION "3:1:0" CACHE STRING "Libedit version info (ignored on MSVC)")

# -------------------------------------------------------------------------
# Generate the headers that were previously built by makelist
# -------------------------------------------------------------------------
set(GENERATED_HEADERS
    ${CMAKE_CURRENT_BINARY_DIR}/vi.h
    ${CMAKE_CURRENT_BINARY_DIR}/emacs.h
    ${CMAKE_CURRENT_BINARY_DIR}/common.h
    ${CMAKE_CURRENT_BINARY_DIR}/fcns.h
    ${CMAKE_CURRENT_BINARY_DIR}/help.h
    ${CMAKE_CURRENT_BINARY_DIR}/func.h
)

set(SCRIPT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vi.h
    COMMAND ${CMAKE_COMMAND} -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/vi.h
            -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/vi.c
            -P ${SCRIPT_DIR}/gen_header.cmake
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/vi.c ${SCRIPT_DIR}/gen_header.cmake
    COMMENT "Generating vi.h"
)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/emacs.h
    COMMAND ${CMAKE_COMMAND} -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/emacs.h
            -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/emacs.c
            -P ${SCRIPT_DIR}/gen_header.cmake
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/emacs.c ${SCRIPT_DIR}/gen_header.cmake
    COMMENT "Generating emacs.h"
)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/common.h
    COMMAND ${CMAKE_COMMAND} -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/common.h
            -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/common.c
            -P ${SCRIPT_DIR}/gen_header.cmake
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/common.c ${SCRIPT_DIR}/gen_header.cmake
    COMMENT "Generating common.h"
)

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fcns.h
  COMMAND ${CMAKE_COMMAND}
          -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/fcns.h
          -DINPUTS="${CMAKE_CURRENT_BINARY_DIR}/vi.h\;${CMAKE_CURRENT_BINARY_DIR}/emacs.h\;${CMAKE_CURRENT_BINARY_DIR}/common.h"
          -P ${SCRIPT_DIR}/gen_fcns.cmake
  DEPENDS
          ${CMAKE_CURRENT_BINARY_DIR}/vi.h
          ${CMAKE_CURRENT_BINARY_DIR}/emacs.h
          ${CMAKE_CURRENT_BINARY_DIR}/common.h
          ${SCRIPT_DIR}/gen_fcns.cmake
  COMMENT "Generating fcns.h (#defines for el_action_t symbols)"
)

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/help.h
  COMMAND ${CMAKE_COMMAND}
          "-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/help.h"
          "-DINPUTS=${CMAKE_CURRENT_SOURCE_DIR}/vi.c\;${CMAKE_CURRENT_SOURCE_DIR}/emacs.c\;${CMAKE_CURRENT_SOURCE_DIR}/common.c"
          -P ${SCRIPT_DIR}/gen_help.cmake
  DEPENDS
          ${CMAKE_CURRENT_SOURCE_DIR}/vi.c
          ${CMAKE_CURRENT_SOURCE_DIR}/emacs.c
          ${CMAKE_CURRENT_SOURCE_DIR}/common.c
          ${SCRIPT_DIR}/gen_help.cmake
  COMMENT "Generating help.h (function help table)"
)

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/func.h
  COMMAND ${CMAKE_COMMAND}
          "-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/func.h"
          "-DINPUTS=${CMAKE_CURRENT_BINARY_DIR}/vi.h\;${CMAKE_CURRENT_BINARY_DIR}/emacs.h\;${CMAKE_CURRENT_BINARY_DIR}/common.h"
          -P ${SCRIPT_DIR}/gen_func.cmake
  DEPENDS
          ${CMAKE_CURRENT_BINARY_DIR}/vi.h
          ${CMAKE_CURRENT_BINARY_DIR}/emacs.h
          ${CMAKE_CURRENT_BINARY_DIR}/common.h
          ${SCRIPT_DIR}/gen_func.cmake
  COMMENT "Generating func.h (function table)"
)

add_custom_target(generate_headers DEPENDS ${GENERATED_HEADERS})

# -------------------------------------------------------------------------
# Core sources
# -------------------------------------------------------------------------
set(LIBEDIT_SOURCES
    chared.c common.c el.c eln.c emacs.c hist.c keymacro.c map.c chartype.c parse.c
    prompt.c read.c refresh.c search.c sig.c terminal.c tty.c vi.c
    reallocarr.c tokenizer.c tokenizern.c
    history.c historyn.c filecomplete.c readline.c chared.h literal.c
    el.h hist.h histedit.h keymacro.h map.h chartype.h parse.h prompt.h
    read.h refresh.h search.h sig.h sys.h terminal.h tty.h vis.h
    filecomplete.h editline/readline.h literal.h
)

if(WIN32)
  list(APPEND LIBEDIT_SOURCES win_ncurses.c utf8.c)
endif()

include(CheckSymbolExists)
check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY)
check_symbol_exists(strlcat "string.h" HAVE_STRLCAT)
check_symbol_exists(memchr "string.h" HAVE_MEMCHR)
check_symbol_exists(memset "string.h" HAVE_MEMSET)
check_symbol_exists(getline "stdio.h" HAVE_GETLINE)
check_symbol_exists(vis "vis.h" HAVE_VIS)
check_symbol_exists(unvis "vis.h" HAVE_UNVIS)
check_symbol_exists(endpwent "pwd.h" HAVE_ENDPWENT)
check_symbol_exists(getpwuid_r "pwd.h" HAVE_GETPW_R_POSIX)
check_symbol_exists(isascii "ctype.h" HAVE_ISASCII)
check_symbol_exists(secure_getenv "stdlib.h" HAVE_SECURE_GETENV)
check_symbol_exists(wcsdup "wchar.h" HAVE_WCSDUP)

include(CheckIncludeFile)
check_include_file("stdint.h" HAVE_STDINT_H)
check_include_file("wchar.h" HAVE_WCHAR_H)
check_include_file("dirent.h" HAVE_DIRENT_H)
check_include_file("dlfcn.h" HAVE_DLFCN_H)
check_include_file("fcntl.h" HAVE_FCNTL_H)
check_include_file("limits.h" HAVE_LIMITS_H)
check_include_file("ncurses.h" HAVE_NCURSES_H)
check_include_file("curses.h" HAVE_CURSES_H)
check_include_file("termcap.h" HAVE_TERMCAP_H)
check_include_file("term.h" HAVE_TERM_H)
check_include_file("sys/wait.h" HAVE_SYS_WAIT_H)
check_include_file("pwd.h" HAVE_PWD_H)
check_include_file("sys/ioctl.h" HAVE_IOCTL_H)
check_include_file("unistd.h" HAVE_UNISTD_H)
check_include_file("sys/param.h" HAVE_SYS_PARAM_H)
check_include_file("strings.h" HAVE_STRINGS_H)

if(NOT WIN32)
include(CheckLibraryExists)
check_library_exists(ncurses tgoto "" HAVE_LIB_NCURSES)
if(HAVE_LIB_NCURSES)
  set(TERMCAP_LIB ncurses)
else()
  check_library_exists(curses  tgoto "" HAVE_LIB_CURSES)
  if(HAVE_LIB_CURSES)
    set(TERMCAP_LIB curses)
  else()
    check_library_exists(termcap tgoto "" HAVE_LIB_TERMCAP)
    if(HAVE_LIB_TERMCAP)
      set(TERMCAP_LIB termcap)
    else()
      check_library_exists(tinfo   tgoto "" HAVE_LIB_TINFO)
      if(HAVE_LIB_TINFO)
        set(TERMCAP_LIB tinfo)
      endif()
    endif()
  endif()
endif()
message(STATUS "Using ${TERMCAP_LIB} for termcap functions")
endif(NOT WIN32)

if(NOT HAVE_STRLCPY)
    list(APPEND LIBEDIT_SOURCES strlcpy.c)
endif()
if(NOT HAVE_STRLCAT)
    list(APPEND LIBEDIT_SOURCES strlcat.c)
endif()
if(NOT HAVE_GETLINE)
    list(APPEND LIBEDIT_SOURCES getline.c)
endif()
if(NOT HAVE_VIS)
    list(APPEND LIBEDIT_SOURCES vis.c)
endif()
if(NOT HAVE_UNVIS)
    list(APPEND LIBEDIT_SOURCES unvis.c)
endif()
if(NOT HAVE_WCSDUP)
    list(APPEND LIBEDIT_SOURCES wcsdup.c)
endif()

# -------------------------------------------------------------------------
# Build library
# -------------------------------------------------------------------------
add_library(edit ${LIBEDIT_SOURCES} ${GENERATED_HEADERS})
target_link_libraries(edit PUBLIC ${TERMCAP_LIB})
set_target_properties(edit PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_dependencies(edit generate_headers)

target_include_directories(edit
    BEFORE
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_BINARY_DIR}
        ${CMAKE_CURRENT_BINARY_DIR}/..
        ${CMAKE_CURRENT_SOURCE_DIR}/editline
)

target_compile_definitions(edit PRIVATE
    _CRT_SECURE_NO_WARNINGS
    _CRT_NONSTDC_NO_WARNINGS
)

install(TARGETS edit
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION bin
)
install(FILES histedit.h editline/readline.h DESTINATION include)

set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${GENERATED_HEADERS})
