ec8246bbc6
It actually makes the binaries smaller, the code easier to read (gems like "val == true", "val == false" are gone) and actually predictable in the sense of that we actually know what we're working with (one bitwise operator was quite adventurous and should now be fixed). This is also more consistent with the other suckless projects around which don't use boolean types.
141 lines
2.1 KiB
C
141 lines
2.1 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <wchar.h>
|
|
|
|
#include "util.h"
|
|
|
|
typedef struct {
|
|
FILE *fp;
|
|
const char *name;
|
|
} Fdescr;
|
|
|
|
static void unexpand(Fdescr *dsc);
|
|
|
|
static int aflag = 0;
|
|
static int tabsize = 8;
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: %s [-a] [-t n] [file ...]\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
Fdescr dsc;
|
|
FILE *fp;
|
|
|
|
ARGBEGIN {
|
|
case 't':
|
|
tabsize = estrtol(EARGF(usage()), 0);
|
|
if (tabsize <= 0)
|
|
eprintf("unexpand: invalid tabsize\n", argv[0]);
|
|
/* Fallthrough: -t implies -a */
|
|
case 'a':
|
|
aflag = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND;
|
|
|
|
if (argc == 0) {
|
|
dsc.name = "<stdin>";
|
|
dsc.fp = stdin;
|
|
unexpand(&dsc);
|
|
} else {
|
|
for (; argc > 0; argc--, argv++) {
|
|
if (!(fp = fopen(*argv, "r"))) {
|
|
weprintf("fopen %s:", *argv);
|
|
continue;
|
|
}
|
|
dsc.name = *argv;
|
|
dsc.fp = fp;
|
|
unexpand(&dsc);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static wint_t
|
|
in(Fdescr *f)
|
|
{
|
|
wint_t c = fgetwc(f->fp);
|
|
|
|
if (c == WEOF && ferror(f->fp))
|
|
eprintf("'%s' read error:", f->name);
|
|
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
out(wint_t c)
|
|
{
|
|
putwchar(c);
|
|
if (ferror(stdout))
|
|
eprintf("write error:");
|
|
}
|
|
|
|
static void
|
|
unexpandspan(unsigned int n, unsigned int col)
|
|
{
|
|
unsigned int off = (col-n) % tabsize;
|
|
|
|
if (n + off >= tabsize && n > 1)
|
|
n += off;
|
|
|
|
for (; n >= tabsize; n -= tabsize)
|
|
out('\t');
|
|
while (n--)
|
|
out(' ');
|
|
}
|
|
|
|
static void
|
|
unexpand(Fdescr *dsc)
|
|
{
|
|
unsigned int n = 0, col = 0;
|
|
int bol = 1;
|
|
wint_t c;
|
|
|
|
while ((c = in(dsc)) != EOF) {
|
|
switch (c) {
|
|
case ' ':
|
|
if (bol || aflag)
|
|
n++;
|
|
col++;
|
|
break;
|
|
case '\t':
|
|
if (bol || aflag)
|
|
n += tabsize - col % tabsize;
|
|
col += tabsize - col % tabsize;
|
|
break;
|
|
case '\b':
|
|
if (bol || aflag)
|
|
unexpandspan(n, col);
|
|
col -= (col > 0);
|
|
n = 0;
|
|
bol = 0;
|
|
break;
|
|
case '\n':
|
|
if (bol || aflag)
|
|
unexpandspan(n, col);
|
|
n = col = 0;
|
|
bol = 1;
|
|
break;
|
|
default:
|
|
if (bol || aflag)
|
|
unexpandspan(n, col);
|
|
n = 0;
|
|
col++;
|
|
bol = 0;
|
|
}
|
|
if ((c != ' ' && c != '\t') || (!aflag && !bol))
|
|
out(c);
|
|
}
|
|
if (n > 0 && (bol || aflag))
|
|
unexpandspan(n, col);
|
|
}
|