builtin / stripspace.con commit xread/xwrite: clip MAX_IO_SIZE to SSIZE_MAX (a983e6a)
   1#include "builtin.h"
   2#include "cache.h"
   3
   4/*
   5 * Returns the length of a line, without trailing spaces.
   6 *
   7 * If the line ends with newline, it will be removed too.
   8 */
   9static size_t cleanup(char *line, size_t len)
  10{
  11        while (len) {
  12                unsigned char c = line[len - 1];
  13                if (!isspace(c))
  14                        break;
  15                len--;
  16        }
  17
  18        return len;
  19}
  20
  21/*
  22 * Remove empty lines from the beginning and end
  23 * and also trailing spaces from every line.
  24 *
  25 * Turn multiple consecutive empty lines between paragraphs
  26 * into just one empty line.
  27 *
  28 * If the input has only empty lines and spaces,
  29 * no output will be produced.
  30 *
  31 * If last line does not have a newline at the end, one is added.
  32 *
  33 * Enable skip_comments to skip every line starting with comment
  34 * character.
  35 */
  36void stripspace(struct strbuf *sb, int skip_comments)
  37{
  38        int empties = 0;
  39        size_t i, j, len, newlen;
  40        char *eol;
  41
  42        /* We may have to add a newline. */
  43        strbuf_grow(sb, 1);
  44
  45        for (i = j = 0; i < sb->len; i += len, j += newlen) {
  46                eol = memchr(sb->buf + i, '\n', sb->len - i);
  47                len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
  48
  49                if (skip_comments && len && sb->buf[i] == comment_line_char) {
  50                        newlen = 0;
  51                        continue;
  52                }
  53                newlen = cleanup(sb->buf + i, len);
  54
  55                /* Not just an empty line? */
  56                if (newlen) {
  57                        if (empties > 0 && j > 0)
  58                                sb->buf[j++] = '\n';
  59                        empties = 0;
  60                        memmove(sb->buf + j, sb->buf + i, newlen);
  61                        sb->buf[newlen + j++] = '\n';
  62                } else {
  63                        empties++;
  64                }
  65        }
  66
  67        strbuf_setlen(sb, j);
  68}
  69
  70static void comment_lines(struct strbuf *buf)
  71{
  72        char *msg;
  73        size_t len;
  74
  75        msg = strbuf_detach(buf, &len);
  76        strbuf_add_commented_lines(buf, msg, len);
  77        free(msg);
  78}
  79
  80static const char *usage_msg = "\n"
  81"  git stripspace [-s | --strip-comments] < input\n"
  82"  git stripspace [-c | --comment-lines] < input";
  83
  84int cmd_stripspace(int argc, const char **argv, const char *prefix)
  85{
  86        struct strbuf buf = STRBUF_INIT;
  87        int strip_comments = 0;
  88        enum { INVAL = 0, STRIP_SPACE = 1, COMMENT_LINES = 2 } mode = STRIP_SPACE;
  89
  90        if (argc == 2) {
  91                if (!strcmp(argv[1], "-s") ||
  92                        !strcmp(argv[1], "--strip-comments")) {
  93                         strip_comments = 1;
  94                } else if (!strcmp(argv[1], "-c") ||
  95                                         !strcmp(argv[1], "--comment-lines")) {
  96                         mode = COMMENT_LINES;
  97                } else {
  98                        mode = INVAL;
  99                }
 100        } else if (argc > 1) {
 101                mode = INVAL;
 102        }
 103
 104        if (mode == INVAL)
 105                usage(usage_msg);
 106
 107        if (strip_comments || mode == COMMENT_LINES)
 108                git_config(git_default_config, NULL);
 109
 110        if (strbuf_read(&buf, 0, 1024) < 0)
 111                die_errno("could not read the input");
 112
 113        if (mode == STRIP_SPACE)
 114                stripspace(&buf, strip_comments);
 115        else
 116                comment_lines(&buf);
 117
 118        write_or_die(1, buf.buf, buf.len);
 119        strbuf_release(&buf);
 120        return 0;
 121}