Re: [dev] [sbase] printf(1)
On Thu, Dec 19, 2013 at 10:49:03PM +, sin wrote: > On Thu, Dec 19, 2013 at 10:18:34PM +, Rob wrote: > > printf '%f\n' 2 > > > > If you "throw it into the standard library's printf", even forgetting > > about how we do that, you'd still interpret 2 as an int, when printf > > expects a float. And that's only the beginning, what about %n, or not > > passing enough arguments? > > Exactly. > > BTW, I just noticed this thread as the messages had been bouncing for some > time > (was migrating my mail setup). is there any update on this? Sorry I do not have the original message so I can't respond there. bye, sin
Re: [dev] [sbase] printf(1)
On Thu, Dec 19, 2013 at 10:18:34PM +, Rob wrote: > printf '%f\n' 2 > > If you "throw it into the standard library's printf", even forgetting > about how we do that, you'd still interpret 2 as an int, when printf > expects a float. And that's only the beginning, what about %n, or not > passing enough arguments? Exactly. BTW, I just noticed this thread as the messages had been bouncing for some time (was migrating my mail setup). bye, sin
Re: [dev] [sbase] printf(1)
Roberto E. Vargas Caballero, Thu, 19 Dec 2013: I'm confused about what you're trying to accomplish here. How about you just copy the format part from the for loop up there and throw it into the standard library's printf? I agree! This strongly smells like NiH-syndrome. Considering we have suckless standard libraries like uClibc, which we could link statically to by default some day in the future, we can safely assume letting the stdlib I think you are lossing something important here. When you execute printf in a program you have the variables which you want to pass to printf, or in the case of vprintf you have the va_list, but in this case you don't have any of them. You cannot call to printf (or vprintf, or sprintf, or vsprintf) with an array of pointers to chars (argv), so you have to forgot the library routines. Exactly. Not to mention, what if the user did this: printf '%f\n' 2 If you "throw it into the standard library's printf", even forgetting about how we do that, you'd still interpret 2 as an int, when printf expects a float. And that's only the beginning, what about %n, or not passing enough arguments? Rob
Re: [dev] [sbase] printf(1)
> That's what I did at first, but I forgot the exact reason why I changed > it :P I probably found kind of wasteful to call putchar() for every > character, but then again, it probably doesn't matter since arguments > for printf usually aren't that long. Remember that putchar is a macro, and it only stores a char in an array and does a comparation (from k&r): #define putc(x,p) (--(p)->cnt >= 0 \ ? (unsigned char) * (p)->ptr++ = (x) : _fillbuf(p)) It is cheap and I think the code is far clear with that aproach. Regards, -- Roberto E. Vargas Caballero
Re: [dev] [sbase] printf(1)
On Thu, Dec 19, 2013 at 02:39:21PM +0100, Roberto E. Vargas Caballero wrote: > You only have to use putchar for every character (except in the case > of the format specifiers), and then you don't need the enp pointer, or > am I missing something else? That's what I did at first, but I forgot the exact reason why I changed it :P I probably found kind of wasteful to call putchar() for every character, but then again, it probably doesn't matter since arguments for printf usually aren't that long.
Re: [dev] [sbase] printf(1)
> I admit, printfmt() needs some cleanup. fmt is supposed to point at the > begining of the conversion string ('%' for a conversion specifier and > '\' for a escape sequence) and end, well, points at the end of the > conversion string (one character after conversion characters like 'd' or > 's'). end is necessary, so you can set "*end = '\0'", otherwise fmt > will include everything up unitl to the end of the complete format > string. You only have to use putchar for every character (except in the case of the format specifiers), and then you don't need the enp pointer, or am I missing something else? Regards, -- Roberto E. Vargas Caballero
Re: [dev] [sbase] printf(1)
On Thu, Dec 19, 2013 at 02:22:53PM +0100, Roberto E. Vargas Caballero wrote: > I also think all the operations with fmt and end are a bit confusing > and maybe it is a bit more clear something like this (taken from a > personal project): I admit, printfmt() needs some cleanup. fmt is supposed to point at the begining of the conversion string ('%' for a conversion specifier and '\' for a escape sequence) and end, well, points at the end of the conversion string (one character after conversion characters like 'd' or 's'). end is necessary, so you can set "*end = '\0'", otherwise fmt will include everything up unitl to the end of the complete format string. I hope I made it clear now (which I'm not so sure of). Best regards Maurice Quennet
Re: [dev] [sbase] printf(1)
> > I'm confused about what you're trying to accomplish here. > > How about you just copy the format part from the for loop up there and > > throw it into the standard library's printf? > > > > I agree! > This strongly smells like NiH-syndrome. Considering we have suckless > standard libraries like uClibc, which we could link statically to by > default some day in the future, we can safely assume letting the stdlib I think you are lossing something important here. When you execute printf in a program you have the variables which you want to pass to printf, or in the case of vprintf you have the va_list, but in this case you don't have any of them. You cannot call to printf (or vprintf, or sprintf, or vsprintf) with an array of pointers to chars (argv), so you have to forgot the library routines. I also think all the operations with fmt and end are a bit confusing and maybe it is a bit more clear something like this (taken from a personal project): void printf(const char *fmt, ...) { unsigned char c; va_list va; int base, sign; va_start(va, fmt); while (( c = *fmt++) != '\0') { if (c == '%') { sign = 0; switch (*fmt++) { case '%': c = '%'; goto printchar; case 'c': c = va_arg(va, char); goto printchar; case 'o': base = 8; break; case 'd': sign = 1; base = 10; break; case 'p': case 'x': base = 16; break; case 's': puts(va_arg(va, char *)); /* passthrou */ default: continue; } printn(va_arg(va, int), base, sign); continue; } printchar: putchar(c); } } Of course, it is a simplified version, and it handles operands from the stack, but it can be easily adapated to argv. Regards, -- Roberto E. Vargas Caballero
Re: [dev] [sbase] printf(1)
On Thu, Dec 19, 2013 at 08:50:46AM +0100, Martti Kühne wrote: > I'm confused about what you're trying to accomplish here. > How about you just copy the format part from the for loop up there and > throw it into the standard library's printf? That's what I'm doing. You still have to figure out, where the format string starts and where it ends and you have to convert the argument to the correct type. Best regards Maurice Quennet
Re: [dev] [sbase] printf(1)
On 2013-12-19, at 10:47, FRIGN wrote: >> Considering we have suckless standard libraries like uClibc, > > Whoops, I meant dietlibc. I kinda mixed up the names. musl FTW! -Truls
Re: [dev] [sbase] printf(1)
On Thu, 19 Dec 2013 10:44:52 +0100 FRIGN wrote: > Considering we have suckless standard libraries like uClibc, Whoops, I meant dietlibc. I kinda mixed up the names. -- FRIGN
Re: [dev] [sbase] printf(1)
On Thu, 19 Dec 2013 08:50:46 +0100 Martti Kühne wrote: > > I'm confused about what you're trying to accomplish here. > How about you just copy the format part from the for loop up there and > throw it into the standard library's printf? > > cheers! > mar77i > I agree! This strongly smells like NiH-syndrome. Considering we have suckless standard libraries like uClibc, which we could link statically to by default some day in the future, we can safely assume letting the stdlib do it is a better choice. Cheers FRIGN -- FRIGN
Re: [dev] [sbase] printf(1)
On Wed, Dec 18, 2013 at 11:36 PM, Maurice Quennet wrote: > Hello dev@, > Hello mjq@, > diff --git a/Makefile b/Makefile > index 2a72a1c..e93f570 100644 [...] > +void > +printfmt(void) > +{ > + int e; > + long l; > + double d; > + char c, f, *tmp; > + > + if (*end == '%') { > + putchar('%'); > + fmt = ++end; > + return; > + } > + > + if (*arg == NULL) > + eprintf("missing argument\n"); > + > + if (*end == 'b') { > + fmt = *arg; > + tmp = end; > + > + while (*fmt) { > + if (*fmt == '\\') { > + ++fmt; > + printesc(); > + continue; > + } > + putchar(*fmt); > + ++fmt; > + } > + > + fmt = end = tmp + 1; > + return; > + } > + > + for (; *end; ++end) { > + if (!isdigit(*end) && *end != '#' && *end != '.' && > + *end != '+' && *end != '-' && *end != ' ') > + break; > + } > + > + if (*end == '\0') > + eprintf("%s: invalid directive\n", fmt); > + > + f = *end; > + c = *++end; > + *end = '\0'; > + > + switch (f) { > + case 'c': > + printf(fmt, **arg++); > + break; > + case 's': > + printf(fmt, *arg++); > + break; > + case 'd': case 'i': case 'o': > + case 'u': case 'X': case 'x': > + e = errno; > + errno = 0; > + l = strtol(*arg, NULL, 0); > + if (errno) > + eprintf("%s: invalid value\n", *arg); > + printf(fmt, l); > + ++arg; > + errno = e; > + break; > + case 'a': case 'A': case 'e': case 'E': > + case 'f': case 'F': case 'g': case 'G': > + e = errno; > + errno = 0; > + d = strtod(*arg, NULL); > + if (errno) > + eprintf("%s: invalid value\n", *arg); > + printf(fmt, d); > + ++arg; > + errno = e; > + break; > + default: > + eprintf("%s: invalid directive\n", fmt); > + } > + > + *end = c; > + fmt = end; > +} > + I'm confused about what you're trying to accomplish here. How about you just copy the format part from the for loop up there and throw it into the standard library's printf? cheers! mar77i
Re: [dev] [sbase] printf(1)
On 2013-12-18 21:06:08 -0600, William Giokas wrote: > People on this list need to learn about 'git format-patch' and 'git > send-email'... ...and about not quoting the entirety of the last message as context when it doesn't provide any. :-) pgpIqSRxp_FhA.pgp Description: PGP signature
Re: [dev] [sbase] printf(1)
On Wed, Dec 18, 2013 at 11:36:12PM +0100, Maurice Quennet wrote: > Hello dev@, > > I was searching for a programming project so I looked into TODO and > picked printf. I hacked something up a few weeks ago, but I didn't > publish it until now, since I didn't have a man page yet and I was a > little busy (a patch follows at the end of the mail). > In the following I will tell a little bit about my implementation and > point out differences to OpenBSD's printf implementation (since I am a > OpenBSD user): > First for my `algorithm': There is nothing much to say. My > implementation simply skips through the format string until it finds a > '\' or a '%' and prints everything before that character. If a '\' was > found, the appropriate escape sequence is printed. If a '%' was found, > it skips forward until it finds a supported conversion specifier, and > passes everything starting from the '%' up until that point to printf(3) > (plus the approprietly converted command line argument). > And now for the differences: For a large part my implementation is > similar to OpenBSD's version. Both support the same escape sequences, > flags and conversion specifiers (which, as far as tested, behave the > same). The major difference is error handling. While OpenBSD's version > prints a warning to stderr and continues parsing its input, my version > exits immediately. Also, my implementation does not check the syntax of > the conversion specifier flags, but simply skips over them. > This is for the following two reasons: > 1.) printf(1) is mostly used for shell scripting, so I think it is ok to > expect the user to pass a well formed format string and to check his > arguments. Even if using a POSIX compliant printf(1), if you put in > garbage you will in return get garbage (or at least not what you > expected to get). > 2.) This way of error handling makes the code simpler/shorter. > For the man page: well, I really suck at writing man pages, especially > since my english is quite shaky. Also, while writing the man page, I > was wondering if it wasn't possible to simply use OpenBSD's man page > (with a few modifications) since both implementations seem to be largely > equivalent. > I have the feeling that I forgot to mention something, but, well, it > probably will come up again. Anyways I hope you like it. > > Best regards, > Maurice Quennet People on this list need to learn about 'git format-patch' and 'git send-email'... -- William Giokas | KaiSforza | http://kaictl.net/ GnuPG Key: 0x73CD09CF Fingerprint: F73F 50EF BBE2 9846 8306 E6B8 6902 06D8 73CD 09CF pgp7WKAm13S8G.pgp Description: PGP signature
[dev] [sbase] printf(1)
Hello dev@, I was searching for a programming project so I looked into TODO and picked printf. I hacked something up a few weeks ago, but I didn't publish it until now, since I didn't have a man page yet and I was a little busy (a patch follows at the end of the mail). In the following I will tell a little bit about my implementation and point out differences to OpenBSD's printf implementation (since I am a OpenBSD user): First for my `algorithm': There is nothing much to say. My implementation simply skips through the format string until it finds a '\' or a '%' and prints everything before that character. If a '\' was found, the appropriate escape sequence is printed. If a '%' was found, it skips forward until it finds a supported conversion specifier, and passes everything starting from the '%' up until that point to printf(3) (plus the approprietly converted command line argument). And now for the differences: For a large part my implementation is similar to OpenBSD's version. Both support the same escape sequences, flags and conversion specifiers (which, as far as tested, behave the same). The major difference is error handling. While OpenBSD's version prints a warning to stderr and continues parsing its input, my version exits immediately. Also, my implementation does not check the syntax of the conversion specifier flags, but simply skips over them. This is for the following two reasons: 1.) printf(1) is mostly used for shell scripting, so I think it is ok to expect the user to pass a well formed format string and to check his arguments. Even if using a POSIX compliant printf(1), if you put in garbage you will in return get garbage (or at least not what you expected to get). 2.) This way of error handling makes the code simpler/shorter. For the man page: well, I really suck at writing man pages, especially since my english is quite shaky. Also, while writing the man page, I was wondering if it wasn't possible to simply use OpenBSD's man page (with a few modifications) since both implementations seem to be largely equivalent. I have the feeling that I forgot to mention something, but, well, it probably will come up again. Anyways I hope you like it. Best regards, Maurice Quennet diff --git a/Makefile b/Makefile index 2a72a1c..e93f570 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ SRC = \ nohup.c\ paste.c\ printenv.c \ + printf.c \ pwd.c \ readlink.c \ renice.c \ diff --git a/printf.1 b/printf.1 new file mode 100644 index 000..e1b8f88 --- /dev/null +++ b/printf.1 @@ -0,0 +1,79 @@ +.TH PRINTF 1 sbase-VERSION +.SH NAME +printf \- formatted output +.SH "SYNOPSIS" +.PP +.B printf +.I format +[ +.I arguments ... +] +.SH DESCRIPTION +.B printf +formats and prints its +.I arguments +as instructed by the +.I format +string. +.P +If an encoding error occurs, such as an unsupported conversion specifier or +escape sequence, or if an argument is missing, +.B printf +will exit immediately without printing any further output. +.SH ESCAPE SEQUENCES +.B printf +supports the following escape sequences: +.TP +.B \ea +bell character +.TP +.B \eb +backspace character +.TP +.B \ee +escape character +.TP +.B \ef +form feed character +.TP +.B \en +new line character +.TP +.B \er +carriage return character +.TP +.B \et +tab character +.TP +.B \ev +vertical tab character +.TP +.B \e' +single quote character +.TP +.B \e\e +backslash character +.TP +.BI \ex hh +ascii character which is represented by the 1- or 2-digit hexadecimal number +.I hh +.TP +.BI \e num +ascii character which is represented by the 1-, 2- or 3-digit octal number +.I num +.SH CONVERSION SPECIFIERS +.B printf +supports the following conversion specifiers: a, A, c, d, e, E, f, F, g, G, i, o, u, x, X. +Since +.B printf +relies on its implementation, see +.IR printf (3) +for a detailed description of those conversion specifiers and their flags. +.P +Additionally the `b' conversion specifier is supported, which takes no flags and +prints its argument, expanding escape sequences. +.SH EXIT VALUES +.B printf +exits 0 on successful completion, and >0 if an error occurs. +.SH SEE ALSO +.IR printf (3) \ No newline at end of file diff --git a/printf.c b/printf.c new file mode 100644 index 000..3516a73 --- /dev/null +++ b/printf.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include + +#include "util.h" + +/* macros from OpenBSD's printf(1) implementation */ +#define isodigit(c) ('0' <= (c) && (c) <= '7') +#define hextobin(c) ('A' <= (c) && (c) <= 'F' ? (c) - 'A' + 10 : \ +'a' <= (c) && (c) <= 'f' ? (c) - 'a' + 10 : (c) - '0') +#define octtobin(c) ((c) - '0') + +static char**arg; +static char *end; +static char *fmt; + +static void printesc(void); +static void printfmt(void); +static void printhex(void); +static void printoct(void); +static void usage(void); + +void +printesc(void) +{ +