aboutsummaryrefslogtreecommitdiff
path: root/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'printf.c')
-rw-r--r--printf.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/printf.c b/printf.c
new file mode 100644
index 0000000..1d14e54
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,99 @@
+#include <unistd.h>
+#include <errno.h>
+
+struct mini_buffer {
+ char *x;
+ unsigned int p, n;
+ int fd;
+};
+
+static char buf_space[512];
+struct mini_buffer b = { buf_space, 0, sizeof(buf_space), 1 };
+
+static void s_write(int fd, char *s, int n) {
+ int w;
+ while (n) {
+ w = write(fd,s,n);
+ if (w==0) _exit(1);
+ if (w==-1) {
+ if (errno == EINTR) continue;
+ _exit(1);
+ }
+ s += w;
+ n -= w;
+ }
+}
+
+void out_flush() { s_write(b.fd, b.x, b.p); b.p=0; }
+void out_char(char ch) { if (b.p==b.n) out_flush(); b.x[b.p] = ch; b.p++; }
+void outs(char *s) { while (*s) { out_char(*s); ++s; } }
+
+static void bad(char *fmt, char *s) {
+ if (b.fd==1) out_flush();
+ b.fd=2;
+ outs("\nbad format: "); outs(fmt); outs("\nstart: ");
+ outs(s); outs("\n"); out_flush(); _exit(2);
+}
+
+char *conv_esc(char *x, char *pr_char) {
+ char *e0 = "ntrabfvEe\\";
+ char *e1 = "\n\t\r\a\b\f\v\033\033\\";
+ char ch;
+ int k;
+ for (k=0; k<10; k++)
+ if (e0[k] == *x)
+ { *pr_char = e1[k]; return x; }
+
+ for (k=0, ch=0; '0'<=*x && *x<'8' && k<3; k++, x++)
+ ch = ch*8 + (*x-'0');
+ if (k == 0) return 0;
+ *pr_char = ch;
+ return --x;
+}
+
+int main(int argc, char **argv) {
+ char *fmt, *s, *x, ch;
+ if (argc<2) _exit(100);
+ fmt = argv[1];
+ argv += 2;
+
+ for (s=fmt; (ch=*s); s++) {
+ if (ch != '%' && ch != '\\') goto print_char;
+ if ((ch = *++s) == 0) goto bad_fmt;
+
+ if (s[-1] == '\\') {
+ if ((x=conv_esc(s,&ch)) == 0) goto bad_fmt;
+ s = x;
+ print_char:
+ out_char(ch);
+ } else { /* % */
+ switch (ch) {
+ case '%': goto print_char;
+ case 's':
+ case 'b':
+ if ((x=*argv++)) {
+ if (ch=='s') outs(x);
+ else { /* %b */
+ char *y, *tmp;
+ for (tmp=x; (ch=*x); x++) {
+ if (ch == '\\') {
+ if ((y=conv_esc(++x,&ch)) == 0) bad(tmp, x-1);
+ x = y;
+ }
+ out_char(ch);
+ }
+ }
+ break;
+ }
+ out_flush(); b.fd=2;
+ outs("\nmissing argument");
+ default:
+ bad_fmt:
+ bad(fmt,s-1);
+ }
+ }
+ }
+ out_flush();
+ fsync(1);
+ _exit(close(1));
+}