Discussion:
[PATCH] Add bc (Last patch)
Gavin Howard
2018-10-29 15:54:33 UTC
Permalink
Hello,

Because my patch is so large, I cannot get it through the mailing list
filter. Therefore, I have pasted it on pastebin, instead.

The link is https://pastebin.com/PJa2vaUR and the raw version can be
found at https://pastebin.com/raw/PJa2vaUR

I look forward to working with the maintainers on getting this added.
(And sorry for the moderator approval spam.)

Gavin Howard
Denys Vlasenko
2018-10-29 22:46:21 UTC
Permalink
Post by Gavin Howard
Hello,
Because my patch is so large, I cannot get it through the mailing list
filter. Therefore, I have pasted it on pastebin, instead.
The link is https://pastebin.com/PJa2vaUR and the raw version can be
found at https://pastebin.com/raw/PJa2vaUR
Probably could send it bzipped.

All functions except bc_main() should be static.
Check with nm --size-sort bc.o


Use <libbb.h>: the entire

+BcStatus bc_read_file(const char *path, char **buf) {
+
+ BcStatus s = BC_STATUS_IO_ERR;
+ FILE *f;
+ size_t size, read;
+ long res;
+ struct stat pstat;
+
+ if (!(f = fopen(path, "r"))) return BC_STATUS_EXEC_FILE_ERR;
+
+ if (fstat(fileno(f), &pstat) == -1) goto malloc_err;
+
+ if (S_ISDIR(pstat.st_mode)) {
+ s = BC_STATUS_PATH_IS_DIR;
+ goto malloc_err;
+ }
+
+ if (fseek(f, 0, SEEK_END) == -1) goto malloc_err;
+ if ((res = ftell(f)) < 0) goto malloc_err;
+ if (fseek(f, 0, SEEK_SET) == -1) goto malloc_err;
+
+ if (!(*buf = malloc((size = (size_t) res) + 1))) {
+ s = BC_STATUS_ALLOC_ERR;
+ goto malloc_err;
+ }
+
+ if ((read = fread(*buf, 1, size, f)) != size) goto read_err;

can be replaced by single xmalloc_open_read_close()!
(The entire
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <getopt.h>
should be replaced by
#include "libbb.h"
#include <if_you_need_anything_not_already_included_by_libbb.h>
)


+ while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt,
&idx)) != -1)
+ {

Use getopt32() ?


+typedef struct BcResult {
+
+ BcResultType t;
+ BcResultData d;
+
+} BcResult;

Lets drop these extra empty lines.


+#ifdef CONFIG_BC
+BcStatus bc_vm_posixError(BcStatus s, const char *file,
+ size_t line, const char *msg);
+#endif // CONFIG_BC

"ifdef CONFIG_foo" is unsafe versus typos. Use "if ENABLE_foo" - it
gives warnings
for typos.


+#ifdef CONFIG_BC
+
+#endif // CONFIG_BC

Why these empty ifdef blocks?


+const char bc_name[] = "bc";

static


+const char *bc_errs[] = {

static const char *const bc_errs[] = {


+ if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;

(1) Please do not nest assignments:

v->v = malloc(esize * BC_VEC_START_CAP);
if (!v->v)
return BC_STATUS_ALLOC_ERR;

(2) Use xmalloc/xzalloc/xrealloc which never return NULL.
We do not try to survive out-of-memory. No check is necessary.


+BcStatus bc_vec_pushByte(BcVec *v, uint8_t data) {
+ return bc_vec_push(v, &data);
+}

The entire bbox code base uses this formatting of function body:

BcStatus bc_vec_pushByte(BcVec *v, uint8_t data)
{
return bc_vec_push(v, &data);
}


+ if (!v->len && (s = bc_vec_pushByte(v, '\0'))) return s;

len is not boolean or a pointer. !foo is usually used with those.
len is an integer. You are testing whether it's zero. Why obfuscate?
For readability, should be:
if (v->len == 0) {
s = bc_vec_pushByte(v, '\0');
if (s)
return s;
}


+ if (a == b) return 0;
+ else if (!a->len) return BC_NUM_NEG(!!b->len, !b->neg);
+ else if (!b->len) return BC_NUM_NEG(1, a->neg);
+ else if (a->neg) {
+ if (b->neg) neg = true;
+ else return -1;
+ }
+ else if (b->neg) return 1;

"else" after exiting code in preceding "if" is not necessary.
Can rewrite as:

if (a == b) return 0;
if (!a->len) return BC_NUM_NEG(!!b->len, !b->neg);
if (!b->len) return BC_NUM_NEG(1, a->neg);
if (a->neg) {
if (!b->neg) return -1;
neg = true;
}
else if (b->neg) return 1;


+static void bc_vm_sig(int sig) {
+ if (sig == SIGINT) {
+ size_t len = strlen(bcg.sig_msg);
+ if (write(2, bcg.sig_msg, len) == (ssize_t) len)
+ bcg.sig += (bcg.signe = bcg.sig == bcg.sigc);
+ }
+ else bcg.sig_other = 1;
+}

+ sa.sa_handler = bc_vm_sig;
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa,
NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
+ {
+ return BC_STATUS_EXEC_SIGACTION_FAIL;
+ }

Calculator has signal handling?!
siganction() never fails - no need to check result.
Better yet, use read_line_input() from libbb.h:
/*
* maxsize must be >= 2.
* Returns:
* -1 on read errors or EOF, or on bare Ctrl-D,
* 0 on ctrl-C (the line entered is still returned in 'command'),
* >0 length of input string, including terminating '\n'
*/
int read_line_input(line_input_t *st, const char *prompt, char
*command, int maxsize) FAST_FUNC;

- it already has Ctrl-C handling, and drop entire signal handling mess.
(It's buggy: write() can modify errno).
Let bc just die on SIGPIPE, SIGTERM and SIGHUP.
Gavin Howard
2018-10-29 23:30:09 UTC
Permalink
Post by Denys Vlasenko
Post by Gavin Howard
Hello,
Because my patch is so large, I cannot get it through the mailing list
filter. Therefore, I have pasted it on pastebin, instead.
The link is https://pastebin.com/PJa2vaUR and the raw version can be
found at https://pastebin.com/raw/PJa2vaUR
Probably could send it bzipped.
I tried, unfortunately. Even though it was under the limit, the
mailing list software rejected.
Post by Denys Vlasenko
All functions except bc_main() should be static.
Check with nm --size-sort bc.o
I will see what I can do about that.
Post by Denys Vlasenko
Use <libbb.h>: the entire
+BcStatus bc_read_file(const char *path, char **buf) {
+
+ BcStatus s = BC_STATUS_IO_ERR;
+ FILE *f;
+ size_t size, read;
+ long res;
+ struct stat pstat;
+
+ if (!(f = fopen(path, "r"))) return BC_STATUS_EXEC_FILE_ERR;
+
+ if (fstat(fileno(f), &pstat) == -1) goto malloc_err;
+
+ if (S_ISDIR(pstat.st_mode)) {
+ s = BC_STATUS_PATH_IS_DIR;
+ goto malloc_err;
+ }
+
+ if (fseek(f, 0, SEEK_END) == -1) goto malloc_err;
+ if ((res = ftell(f)) < 0) goto malloc_err;
+ if (fseek(f, 0, SEEK_SET) == -1) goto malloc_err;
+
+ if (!(*buf = malloc((size = (size_t) res) + 1))) {
+ s = BC_STATUS_ALLOC_ERR;
+ goto malloc_err;
+ }
+
+ if ((read = fread(*buf, 1, size, f)) != size) goto read_err;
can be replaced by single xmalloc_open_read_close()!
Will do.
Post by Denys Vlasenko
(The entire
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <getopt.h>
should be replaced by
#include "libbb.h"
#include <if_you_need_anything_not_already_included_by_libbb.h>
)
Sorry about that. I tried to follow the documentation as exactly as I could.
Post by Denys Vlasenko
+ while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt,
&idx)) != -1)
+ {
Use getopt32() ?
Okay. Will do.
Post by Denys Vlasenko
+typedef struct BcResult {
+
+ BcResultType t;
+ BcResultData d;
+
+} BcResult;
Lets drop these extra empty lines.
Will do.
Post by Denys Vlasenko
+#ifdef CONFIG_BC
+BcStatus bc_vm_posixError(BcStatus s, const char *file,
+ size_t line, const char *msg);
+#endif // CONFIG_BC
"ifdef CONFIG_foo" is unsafe versus typos. Use "if ENABLE_foo" - it
gives warnings
for typos.
Oh? Did not know that.
Post by Denys Vlasenko
+#ifdef CONFIG_BC
+
+#endif // CONFIG_BC
Why these empty ifdef blocks?
This was caused by bugs in my release script that have been fixed.
Post by Denys Vlasenko
+const char bc_name[] = "bc";
static
+const char *bc_errs[] = {
static const char *const bc_errs[] = {
Will do.
Post by Denys Vlasenko
+ if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;
v->v = malloc(esize * BC_VEC_START_CAP);
if (!v->v)
return BC_STATUS_ALLOC_ERR;
Oh boy. I was afraid of this.

The problem is that I am also trying to get this bc into toybox (might
as well reduce duplication of effort), and as I am sure you know,
Landley is very particular about the line count of commands. To be
honest, I would prefer your style (except for putting if statements
that on one line if possible), but this reduces line count.

I don't really know what to do here.
Post by Denys Vlasenko
(2) Use xmalloc/xzalloc/xrealloc which never return NULL.
We do not try to survive out-of-memory. No check is necessary.
Will do. I was not aware of those functions. bc does have to print a
diagnostic message (see
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_15),
but I checked that those procedures do that.
Post by Denys Vlasenko
+BcStatus bc_vec_pushByte(BcVec *v, uint8_t data) {
+ return bc_vec_push(v, &data);
+}
BcStatus bc_vec_pushByte(BcVec *v, uint8_t data)
{
return bc_vec_push(v, &data);
}
This one might be a little harder.
Post by Denys Vlasenko
+ if (!v->len && (s = bc_vec_pushByte(v, '\0'))) return s;
len is not boolean or a pointer. !foo is usually used with those.
len is an integer. You are testing whether it's zero. Why obfuscate?
if (v->len == 0) {
s = bc_vec_pushByte(v, '\0');
if (s)
return s;
}
+ if (a == b) return 0;
+ else if (!a->len) return BC_NUM_NEG(!!b->len, !b->neg);
+ else if (!b->len) return BC_NUM_NEG(1, a->neg);
+ else if (a->neg) {
+ if (b->neg) neg = true;
+ else return -1;
+ }
+ else if (b->neg) return 1;
"else" after exiting code in preceding "if" is not necessary.
if (a == b) return 0;
if (!a->len) return BC_NUM_NEG(!!b->len, !b->neg);
if (!b->len) return BC_NUM_NEG(1, a->neg);
if (a->neg) {
if (!b->neg) return -1;
neg = true;
}
else if (b->neg) return 1;
You are correct.
Post by Denys Vlasenko
+static void bc_vm_sig(int sig) {
+ if (sig == SIGINT) {
+ size_t len = strlen(bcg.sig_msg);
+ if (write(2, bcg.sig_msg, len) == (ssize_t) len)
+ bcg.sig += (bcg.signe = bcg.sig == bcg.sigc);
+ }
+ else bcg.sig_other = 1;
+}
+ sa.sa_handler = bc_vm_sig;
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa,
NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
+ {
+ return BC_STATUS_EXEC_SIGACTION_FAIL;
+ }
Calculator has signal handling?!
siganction() never fails - no need to check result.
GNU bc has signal handling. It is so users can stop long-running
calculations. This bc is meant to be compatible.
Post by Denys Vlasenko
/*
* maxsize must be >= 2.
* -1 on read errors or EOF, or on bare Ctrl-D,
* 0 on ctrl-C (the line entered is still returned in 'command'),
* >0 length of input string, including terminating '\n'
*/
int read_line_input(line_input_t *st, const char *prompt, char
*command, int maxsize) FAST_FUNC;
- it already has Ctrl-C handling, and drop entire signal handling mess.
(It's buggy: write() can modify errno).
Oh, thanks for the heads up about write. I will take care of that, but
to remain compatible with GNU, I will still have to do a write.
Post by Denys Vlasenko
Let bc just die on SIGPIPE, SIGTERM and SIGHUP.
I guess I could do that.

Okay, as it stands, I see one big showstopper for getting this into
busybox: code style. I don't know how to fix code style with a script,
so if it is a showstopper for you (and if it is, I don't blame you), I
guess I have to respectfully withdraw my patch and apologize for
wasting your time.

If, however, it is not, we can talk about what to do about it. And I
will get started on changing the other things.

Gavin Howard
Lauri Kasanen
2018-10-30 07:11:24 UTC
Permalink
On Mon, 29 Oct 2018 17:30:09 -0600
Post by Gavin Howard
Okay, as it stands, I see one big showstopper for getting this into
busybox: code style. I don't know how to fix code style with a script,
so if it is a showstopper for you (and if it is, I don't blame you), I
guess I have to respectfully withdraw my patch and apologize for
wasting your time.
If, however, it is not, we can talk about what to do about it. And I
will get started on changing the other things.
Install GNU indent. It has an option for where to put the braces.

- Lauri
Denys Vlasenko
2018-10-30 09:31:52 UTC
Permalink
Post by Gavin Howard
Post by Denys Vlasenko
Post by Gavin Howard
Because my patch is so large, I cannot get it through the mailing list
filter. Therefore, I have pasted it on pastebin, instead.
The link is https://pastebin.com/PJa2vaUR and the raw version can be
found at https://pastebin.com/raw/PJa2vaUR
Probably could send it bzipped.
I tried, unfortunately. Even though it was under the limit, the
mailing list software rejected.
Ok, pastebin would work for the time being.
Post by Gavin Howard
Post by Denys Vlasenko
+ if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;
v->v = malloc(esize * BC_VEC_START_CAP);
if (!v->v)
return BC_STATUS_ALLOC_ERR;
Oh boy. I was afraid of this.
The problem is that I am also trying to get this bc into toybox (might
as well reduce duplication of effort), and as I am sure you know,
Landley is very particular about the line count of commands. To be
honest, I would prefer your style (except for putting if statements
that on one line if possible), but this reduces line count.
I don't really know what to do here.
I can rewrite these constructs after patch is merged.
Post by Gavin Howard
Post by Denys Vlasenko
+BcStatus bc_vec_pushByte(BcVec *v, uint8_t data) {
+ return bc_vec_push(v, &data);
+}
BcStatus bc_vec_pushByte(BcVec *v, uint8_t data)
{
return bc_vec_push(v, &data);
}
This one might be a little harder.
I can rewrite these constructs after patch is merged.
Post by Gavin Howard
Post by Denys Vlasenko
+ if (!v->len && (s = bc_vec_pushByte(v, '\0'))) return s;
len is not boolean or a pointer. !foo is usually used with those.
len is an integer. You are testing whether it's zero. Why obfuscate?
if (v->len == 0) {
s = bc_vec_pushByte(v, '\0');
if (s)
return s;
}
One-line "if (s) return s;" is okay too, when both if-expression and
statement are simple.
Post by Gavin Howard
Post by Denys Vlasenko
+static void bc_vm_sig(int sig) {
+ if (sig == SIGINT) {
+ size_t len = strlen(bcg.sig_msg);
+ if (write(2, bcg.sig_msg, len) == (ssize_t) len)
+ bcg.sig += (bcg.signe = bcg.sig == bcg.sigc);
+ }
+ else bcg.sig_other = 1;
+}
+ sa.sa_handler = bc_vm_sig;
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa,
NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
+ {
+ return BC_STATUS_EXEC_SIGACTION_FAIL;
+ }
Calculator has signal handling?!
siganction() never fails - no need to check result.
GNU bc has signal handling. It is so users can stop long-running
calculations. This bc is meant to be compatible.
Well, without signal handling ^C will stop long-running calculations
too. :)
I can imagine some users who use interactive bc *a lot* (scientist?),
and who would want "^C abort calc but stays in bc" behavior,
but they are likely a minority. Can you make signal handling optional?
Post by Gavin Howard
Okay, as it stands, I see one big showstopper for getting this into
busybox: code style. I don't know how to fix code style with a script,
so if it is a showstopper for you
It is not.
Gavin Howard
2018-10-30 15:51:41 UTC
Permalink
Post by Denys Vlasenko
Post by Gavin Howard
Post by Denys Vlasenko
Post by Gavin Howard
Because my patch is so large, I cannot get it through the mailing list
filter. Therefore, I have pasted it on pastebin, instead.
The link is https://pastebin.com/PJa2vaUR and the raw version can be
found at https://pastebin.com/raw/PJa2vaUR
Probably could send it bzipped.
I tried, unfortunately. Even though it was under the limit, the
mailing list software rejected.
Ok, pastebin would work for the time being.
Post by Gavin Howard
Post by Denys Vlasenko
+ if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;
v->v = malloc(esize * BC_VEC_START_CAP);
if (!v->v)
return BC_STATUS_ALLOC_ERR;
Oh boy. I was afraid of this.
The problem is that I am also trying to get this bc into toybox (might
as well reduce duplication of effort), and as I am sure you know,
Landley is very particular about the line count of commands. To be
honest, I would prefer your style (except for putting if statements
that on one line if possible), but this reduces line count.
I don't really know what to do here.
I can rewrite these constructs after patch is merged.
Post by Gavin Howard
Post by Denys Vlasenko
+BcStatus bc_vec_pushByte(BcVec *v, uint8_t data) {
+ return bc_vec_push(v, &data);
+}
BcStatus bc_vec_pushByte(BcVec *v, uint8_t data)
{
return bc_vec_push(v, &data);
}
This one might be a little harder.
I can rewrite these constructs after patch is merged.
Post by Gavin Howard
Post by Denys Vlasenko
+ if (!v->len && (s = bc_vec_pushByte(v, '\0'))) return s;
len is not boolean or a pointer. !foo is usually used with those.
len is an integer. You are testing whether it's zero. Why obfuscate?
if (v->len == 0) {
s = bc_vec_pushByte(v, '\0');
if (s)
return s;
}
One-line "if (s) return s;" is okay too, when both if-expression and
statement are simple.
Post by Gavin Howard
Post by Denys Vlasenko
+static void bc_vm_sig(int sig) {
+ if (sig == SIGINT) {
+ size_t len = strlen(bcg.sig_msg);
+ if (write(2, bcg.sig_msg, len) == (ssize_t) len)
+ bcg.sig += (bcg.signe = bcg.sig == bcg.sigc);
+ }
+ else bcg.sig_other = 1;
+}
+ sa.sa_handler = bc_vm_sig;
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa,
NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
+ {
+ return BC_STATUS_EXEC_SIGACTION_FAIL;
+ }
Calculator has signal handling?!
siganction() never fails - no need to check result.
GNU bc has signal handling. It is so users can stop long-running
calculations. This bc is meant to be compatible.
Well, without signal handling ^C will stop long-running calculations
too. :)
I can imagine some users who use interactive bc *a lot* (scientist?),
and who would want "^C abort calc but stays in bc" behavior,
but they are likely a minority. Can you make signal handling optional?
Post by Gavin Howard
Okay, as it stands, I see one big showstopper for getting this into
busybox: code style. I don't know how to fix code style with a script,
so if it is a showstopper for you
It is not.
Thank you for being willing. I can work with this. It may take me a
few days to generate a new patch.

Gavin Howard
d***@manrolandgoss.com
2018-10-31 07:04:02 UTC
Permalink
Sent: Tuesday, October 30, 2018 12:30 AM
Post by Denys Vlasenko
...
+ if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;
v->v = malloc(esize * BC_VEC_START_CAP);
if (!v->v)
return BC_STATUS_ALLOC_ERR;
Oh boy. I was afraid of this.
The problem is that I am also trying to get this bc into toybox (might
as well reduce duplication of effort), and as I am sure you know,
Landley is very particular about the line count of commands. To be
honest, I would prefer your style (except for putting if statements
that on one line if possible), but this reduces line count.
I don't really know what to do here.
If by "nest" is meant "nest in another pair of parentheses", the two requirements are accordable:

if (v->v = malloc(esize * BC_VEC_START_CAP), !v->v) ...
--
Regards,
Dietmar Schindler
________________________________
manroland Goss web systems GmbH | Managing Director: Alexander Wassermann
Registered Office: Augsburg | Trade Register: AG Augsburg | HRB-No.: 32609 | VAT: DE815764857

Confidentiality note:
This message and any attached documents may contain confidential or proprietary information of manroland|Goss. These materials are intended only for the use of the intended recipient. If you are not the intended recipient of this transmission, you are hereby notified that any distribution, disclosure, printing, copying, storage, modification or the taking of any action in reliance upon this transmission is strictly prohibited. Delivery of this message to any person other than the intended recipient shall not compromise or waive such confidentiality, privilege or exemption from disclosure as to this communication. If you have received this communication in error, please immediately notify the sender and delete the message from your system. All liability for viruses is excluded to the fullest extent permitted by law.
________________________________

Loading...