/*  Functions from hack's utils library.
    Copyright (C) 1989-2020 Free Software Foundation, Inc.

    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 3, 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, see <https://www.gnu.org/licenses/>. */

#include <stdio.h>

#include "basicdefs.h"

#include <fcntl.h>
#ifndef O_BINARY
# ifdef _O_BINARY
#  define O_BINARY _O_BINARY
#  define setmode  _setmode
# endif
#endif

#if (O_BINARY - 0)
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
# ifndef S_ISCHR
#  define S_ISCHR(m)  (((m) & S_IFMT) == S_IFCHR)
# endif
# if defined (__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define __gnuc_extension__  __extension__
# else
#  define __gnuc_extension__
# endif
# if __DJGPP__
#  include <crt0.h>
extern int _crt0_startup_flags;

#  undef  IS_DIR_SEPARATOR
#  define IS_DIR_SEPARATOR(c)  ((c) == '/' || (c) == '\\' || (c) == ':')

#  include <libc/unconst.h>
#  define STRIP_FULL_PATH_AND_EXTENSION(file_name)        \
   (__gnuc_extension__                                    \
     ({                                                   \
        char *_dst, *_src;                                \
        _dst = _src = unconst((file_name), char *);       \
        while (*_src++)                                   \
          ;                                               \
        while ((_src - _dst) && (*--_src != '.'))         \
          ;                                               \
        for (*_src = '\0'; (_src - _dst); _src--)         \
          if (IS_DIR_SEPARATOR(*_src))                    \
            break;                                        \
        if (_src - _dst)                                  \
          while ((*_dst++ = *++_src))                     \
            ;                                             \
        (file_name);                                      \
     })                                                   \
   )

#  define STRIP_EXTENSION(file_name)                      \
   (__gnuc_extension__                                    \
     ({                                                   \
        char *_begin, *_end;                              \
        _begin = _end = unconst((file_name), char *);     \
        while (*_end++)                                   \
          ;                                               \
        while ((_end - _begin) && (*--_end != '.'))       \
          ;                                               \
        if (*_end == '.')                                 \
          *_end = '\0';                                   \
        (file_name);                                      \
     })                                                   \
   )

#  define CANONICALIZE_PATH(path)                         \
   (__gnuc_extension__                                    \
     ({                                                   \
        if ((path))                                       \
        {                                                 \
          char *_p = unconst((path), char *);             \
          for (; *_p; _p++)                               \
            if (*_p == '\\')                              \
              *_p = '/';                                  \
        }                                                 \
        (path);                                           \
     })                                                   \
   )

#if 0
/*
 *  Define __djgpp_progname.
 *  This is used as global variable by getprogname.
 */
char __djgpp_program_invocation_name[FILENAME_MAX];
#  define initialize_main(argcp, argvp)                   \
   (__gnuc_extension__                                    \
     ({                                                   \
        char *_name = CANONICALIZE_PATH((*(argvp))[0]);   \
                                                          \
        int i;                                            \
        for (i = 0; _name[i] && i < FILENAME_MAX; i++)    \
          __djgpp_program_invocation_name[i] = _name[i];  \
        for (; i; i--)                                    \
          if (__djgpp_program_invocation_name[i] == '.')  \
          {                                               \
            __djgpp_program_invocation_name[i] = '\0';    \
            break;                                        \
          }                                               \
     })                                                   \
   )
#endif

   /*
    *  For some non posix systems where multiple cmds
    *  separated by semicolon must be explicitly
    *  enabled before calling system().
    */
#  define system_flags                              __system_flags
#  define system_allow_multiple_cmds                __system_allow_multiple_cmds
#  define SYSTEM_CALL_NEED_MULTIPLE_CMDS_ENABLING   1
#  define IS_TMPDIR(dirname)                        (!((dirname) == NULL || access((dirname), D_OK)))
extern int __system_flags;

void xfree (void *ptr);
# else /*  !__DJGPP__  */
#  define STRIP_FULL_PATH_AND_EXTENSION(file_name)  (file_name)
#  define STRIP_EXTENSION(file_name)                (file_name)
#  define CANONICALIZE_PATH(path)                   (path)
#  define SYSTEM_CALL_NEED_MULTIPLE_CMDS_ENABLING   0
#  define IS_TMPDIR(dirname)                        ((dirname) != NULL)
# endif /*  !__DJGPP__  */
# define IS_SLASH(C)                                ((C) == '/' || (C) == '\\')
# define LAST_SLASH(filename)             \
  (__gnuc_extension__                     \
    ({                                    \
       char *_pb, *_ps;                   \
       _pb = strrchr((filename), '\\');   \
       _ps = strrchr((filename), '/');    \
       if (!_pb && !_ps)                  \
         _ps = strrchr((filename), ':');  \
       else if (_pb > _ps) _ps = _pb;     \
       _ps;                               \
    })                                    \
  )
# define CLOSE_FILE_TO_UNLINK(fd)         \
  (__gnuc_extension__                     \
    ({                                    \
       int _rc = close((fd));             \
       _rc;                               \
    })                                    \
  )

extern void register_cleanup_file_descriptor(const int fd);
#else  /* !O_BINARY */
  /*  posix  */
# define STRIP_FULL_PATH_AND_EXTENSION(file_name)   (file_name)
# define STRIP_EXTENSION(file_name)                 (file_name)
# define CANONICALIZE_PATH(path)                    (path)
# define SYSTEM_CALL_NEED_MULTIPLE_CMDS_ENABLING    0
# define IS_TMPDIR(dirname)                         ((dirname) != NULL)
# define IS_SLASH(C)                                ((C) == '/')
# define LAST_SLASH(filename)                       (strrchr((filename), '/'))
# define CLOSE_FILE_TO_UNLINK(fd)                   do {/* void */} while(0)
#endif /* !O_BINARY */

#define LFN_FILE_SYSTEM(filename)  (pathconf((filename), _PC_NAME_MAX) > 12)


enum exit_codes {
                      /* EXIT_SUCCESS is already defined as 0 */
  EXIT_BAD_USAGE = 1, /* bad program syntax, invalid command-line options */
  EXIT_BAD_INPUT = 2, /* failed to open some of the input files */
  EXIT_PANIC     = 4  /* PANIC during program execution */
};


_Noreturn void panic (const char *str, ...) _GL_ATTRIBUTE_FORMAT_PRINTF (1, 2);

void set_read_mode (FILE *stream);
FILE *ck_fopen (const char *name, const char *mode, int fail);
FILE *ck_fdopen (int fd, const char *name, const char *mode, int fail);
void ck_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t ck_fread (void *ptr, size_t size, size_t nmemb, FILE *stream);
void ck_fflush (FILE *stream);
void ck_fclose (FILE *stream);
const char *follow_symlink (const char *path);
size_t ck_getdelim (char **text, size_t *buflen, char buffer_delimiter,
                    FILE *stream);
FILE * ck_mkstemp (char **p_filename, const char *tmpdir, const char *base,
                   const char *mode) _GL_ARG_NONNULL ((1, 2, 3, 4));
void ck_rename (const char *from, const char *to, const char *unlink_if_fail);

void *ck_malloc (size_t size);
void *ck_realloc (void *ptr, size_t size);

struct buffer *init_buffer (void);
char *get_buffer (struct buffer const *b) _GL_ATTRIBUTE_PURE;
size_t size_buffer (struct buffer const *b) _GL_ATTRIBUTE_PURE;
char *add_buffer (struct buffer *b, const char *p, size_t n);
char *add1_buffer (struct buffer *b, int ch);
void free_buffer (struct buffer *b);
