text: /*-
   * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
   *
   * Copyright (c) 2021 Tobias Kortkamp <tobik@FreeBSD.org>
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  #include "config.h"
  
  #include <sys/param.h>
  #include <errno.h>
  #include <inttypes.h>
  #include <stdbool.h>
  #define _WITH_GETLINE
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  
  #include "flow.h"
  #include "io.h"
  #include "mem.h"
  #include "mempool.h"
  #include "str.h"
  #include "util.h"
  
  struct LineIterator {
  	FILE *f;
  	char *line;
  	size_t linecap;
  	ssize_t linelen;
  
  	bool forwarded;
  	size_t i;
  	size_t len;
  };
  
  bool
  can_use_colors(FILE *fp)
  {
  	if (getenv("CLICOLOR_FORCE") != NULL) {
  		return true;
  	}
  
  	if (getenv("NO_COLOR") != NULL) {
  		return false;
  	}
  
  	unless (fp) {
  		return false;
  	}
  
  	if (isatty(fileno(fp)) == 0) {
  		return false;
  	}
  
  	return true;
  }
  
  struct LineIterator *
  line_iterator(FILE *f, ssize_t a, ssize_t b)
  {
  	struct LineIterator *iter = xmalloc(sizeof(struct LineIterator));
  	slice_to_range(SSIZE_MAX, a, b, &iter->i, &iter->len);
  	iter->f = f;
  	return iter;
  }
  
  void
  line_iterator_cleanup(struct LineIterator **iter_)
  {
  	struct LineIterator *iter = *iter_;
  	if (iter != NULL) {
  		free(iter->line);
  		free(iter);
  		*iter_ = NULL;
  	}
  }
  
  bool
  line_iterator_next(struct LineIterator **iter_, size_t *index, char **line, size_t *linelen)
  {
  	struct LineIterator *iter = *iter_;
  
  	unless (iter->forwarded) {
  		iter->forwarded = true;
  		for (size_t i = 0; i < iter->i; i++) {
  			unless ((iter->linelen = getline(&iter->line, &iter->linecap, iter->f)) > 0) {
  				goto done;
  			}
  		}
  	}
  
  	if ((iter->linelen = getline(&iter->line, &iter->linecap, iter->f)) > 0 && iter->i < iter->len) {
  		if (iter->linelen > 0 && iter->line[iter->linelen - 1] == '\n') {
  			iter->line[iter->linelen - 1] = 0;
  			iter->linelen--;
  		}
  		*index = iter->i++;
  		*line = iter->line;
  		*linelen = iter->linelen;
  		return true;
  	}
  
  done:
  	line_iterator_cleanup(iter_);
  	*iter_ = NULL;
  	return false;
  }
  
  char *
  slurp(FILE *f, struct Mempool *pool)
  {
  #define SLURP_BUF_SIZE	(8*1024*1024)
  	size_t bufsize = SLURP_BUF_SIZE + 1;
  	char *buf = xrecallocarray(NULL, 0, bufsize, 1);
  	size_t left = SLURP_BUF_SIZE;
  	ssize_t bytes;
  	size_t pos = 0;
  	while ((bytes = fread(buf + pos, left, 1, f)) != 0) {
  		if (bytes < 0) {
  			if (errno == EAGAIN) {
  				continue;
  			}
  			free(buf);
  			return NULL;
  		}
  		left -= bytes;
  		pos += bytes;
  		if (left == 0) {
  			size_t oldsize = bufsize;
  			bufsize += SLURP_BUF_SIZE;
  			left = SLURP_BUF_SIZE;
  			buf = xrecallocarray(buf, oldsize, bufsize, 1);
  		}
  	}
  
  	return mempool_take(pool, buf);
  }
  
  char *
  symlink_read(int dir, const char *path, struct Mempool *pool)
  {
  	char buf[PATH_MAX];
  	ssize_t len = readlinkat(dir, path, buf, sizeof(buf));
  	if (len != -1) {
  		return str_ndup(pool, buf, len);
  	}
  	return NULL;
  }
  
  bool
  symlink_update(int dir, const char *path1, const char *path2, struct Mempool *pool, char **prev)
  {
  	if (prev != NULL) {
  		*prev = NULL;
  	}
  	while (symlinkat(path1, dir, path2) == -1) {
  		if (errno == EEXIST) {
  			if (prev != NULL) {
  				*prev = symlink_read(dir, path2, pool);
  			}
  			if (unlinkat(dir, path2, 0) == -1) {
  				if (prev != NULL) {
  					*prev = NULL;
  				}
  				return false;
  			}
  		} else {
  			if (prev != NULL) {
  				*prev = NULL;
  			}
  			return false;
  		}
  	}
  
  	return true;
  }
  