.
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PREFIX ?= /usr/local
2 | BINDIR = $(DESTDIR)$(PREFIX)/bin
3 | MANDIR = $(DESTDIR)$(PREFIX)/share/man/man1
4 | DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/bcal
5 | STRIP ?= strip
6 |
7 | CFLAGS_OPTIMIZATION ?= -O3
8 | CFLAGS_WARNINGS ?= -Wall -Wextra -Wno-unused-parameter -Werror
9 |
10 | LDLIBS_READLINE ?= -lreadline
11 | LDLIBS_EDITLINE ?= -ledit
12 |
13 | CFLAGS += $(CFLAGS_OPTIMIZATION) $(CFLAGS_WARNINGS)
14 |
15 | O_EL := 0 # set to use the BSD editline library
16 |
17 | ifeq ($(strip $(O_EL)),1)
18 | LDLIBS += $(LDLIBS_EDITLINE)
19 | else
20 | LDLIBS += $(LDLIBS_READLINE)
21 | endif
22 |
23 | SRC = $(wildcard src/*.c)
24 | INCLUDE = -Iinc
25 |
26 | bcal: $(SRC)
27 | $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(INCLUDE) -o bcal $(SRC) $(LDLIBS)
28 |
29 | all: bcal
30 |
31 | x86: $(SRC)
32 | $(CC) -m64 $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(INCLUDE) -o bcal $(SRC) $(LDLIBS)
33 | strip bcal
34 |
35 | distclean: clean
36 | rm -f *~
37 |
38 | install: bcal
39 | install -m 0755 -d $(BINDIR)
40 | install -m 0755 -d $(MANDIR)
41 | install -m 0755 -d $(DOCDIR)
42 | install -m 0755 bcal $(BINDIR)
43 | gzip -c bcal.1 > bcal.1.gz
44 | install -m 0644 bcal.1.gz $(MANDIR)
45 | install -m 0644 README.md $(DOCDIR)
46 | rm -f bcal.1.gz
47 |
48 | uninstall:
49 | rm -f $(BINDIR)/bcal
50 | rm -f $(MANDIR)/bcal.1.gz
51 | rm -rf $(DOCDIR)
52 |
53 | strip: bcal
54 | $(STRIP) $^
55 |
56 | clean:
57 | -rm -f bcal
58 |
59 | skip: ;
60 |
61 | .PHONY: all x86 distclean install uninstall strip clean
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | bcal
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | `bcal` (*Byte CALculator*) is a REPL CLI utility for storage expression (e.g. `"(2GiB * 2) / (2KiB >> 2)"`) evaluation, SI/IEC conversion, byte address calculation, base conversion and LBA/CHS calculation. It's very useful for those who deal with bits, bytes, addresses and binary prefixes frequently.
17 |
18 | It has a [`bc`](https://www.gnu.org/software/bc/manual/html_mono/bc.html) mode for general-purpose numerical calculations. Alternatively, it can also invoke the more featured [`calc`](http://www.isthe.com/chongo/tech/comp/calc/) which works better with expressions involving multiple bases.
19 |
20 | `bcal` uses [SI and IEC binary prefixes](https://en.wikipedia.org/wiki/Binary_prefix) and supports 64-bit Operating Systems only.
21 |
22 | ### Table of Contents
23 |
24 | - [Features](#features)
25 | - [Installation](#installation)
26 | - [Dependencies](#dependencies)
27 | - [From a package manager](#from-a-package-manager)
28 | - [From source](#from-source)
29 | - [Termux](#termux)
30 | - [Usage](#usage)
31 | - [cmdline options](#cmdline-options)
32 | - [Operational notes](#operational-notes)
33 | - [Examples](#examples)
34 | - [Testing](#testing)
35 | - [Copyright](#copyright)
36 |
37 | ### Features
38 |
39 | - REPL and single execution modes
40 | - evaluate arithmetic expressions involving storage units
41 | - perform general purpose calculations (using bc or calc)
42 | - works with piped input or file redirection
43 | - convert to IEC/SI standard data storage units
44 | - interactive mode with the last valid result stored for reuse
45 | - show the address in bytes
46 | - show address as LBA:OFFSET
47 | - convert CHS to LBA and *vice versa*
48 | - base conversion to binary, decimal and hex
49 | - custom sector size, max heads/cylinder and max sectors/track
50 | - minimal dependencies
51 |
52 | ### Installation
53 |
54 | #### Dependencies
55 |
56 | `bcal` is written in C and depends on standard libc and GNU Readline (or [BSD Editline](https://www.thrysoee.dk/editline/)). It invokes GNU `bc` or `calc` for non-storage expressions.
57 |
58 | To use `calc`:
59 |
60 | export BCAL_USE_CALC=1
61 |
62 | #### From a package manager
63 |
64 | Install `bcal` from your package manager. If the version available is dated try an alternative installation method.
65 |
66 | Packaging status (expand)
67 |
68 |
69 |
70 |
71 |
72 |
73 | #### From source
74 |
75 | If you have git installed, clone this repository. Otherwise, download the [latest stable release](https://github.com/jarun/bcal/releases/latest) or [development version](https://github.com/jarun/bcal/archive/master.zip) (*risky*).
76 |
77 | Install to default location (`/usr/local`):
78 |
79 | $ sudo make strip install
80 | To link to libedit:
81 |
82 | $ sudo make O_EL=1 strip install
83 | To uninstall, run:
84 |
85 | $ sudo make uninstall
86 |
87 | `PREFIX` is supported, in case you want to install to a different location.
88 |
89 | #### Termux
90 |
91 | `bcal` can be compiled and installed from source in the Termux environment on `aarch64` Android devices. Instructions:
92 |
93 | ```
94 | $ wget https://github.com/jarun/bcal/archive/master.zip
95 | $ unzip bcal-master.zip
96 | $ cd bcal-master/
97 | $ pkg install make clang readline-dev
98 | $ make strip install
99 | ```
100 |
101 | ### Usage
102 |
103 | #### cmdline options
104 |
105 | ```
106 | usage: bcal [-c N] [-f loc] [-s bytes] [expr]
107 | [N [unit]] [-b [expr]] [-m] [-d] [-h]
108 |
109 | Storage expression calculator.
110 |
111 | positional arguments:
112 | expr expression in decimal/hex operands
113 | N [unit] capacity in B/KiB/MiB/GiB/TiB/kB/MB/GB/TB
114 | https://en.wikipedia.org/wiki/Binary_prefix
115 | default unit is B (byte), case is ignored
116 | N can be decimal or '0x' prefixed hex value
117 |
118 | optional arguments:
119 | -c N show +ve integer N in binary, decimal, hex
120 | -f loc convert CHS to LBA or LBA to CHS
121 | refer to the operational notes in man page
122 | -s bytes sector size [default 512]
123 | -b [expr] enter bc mode or evaluate expression in bc
124 | -m show minimal output (e.g. decimal bytes)
125 | -d enable debug information and logs
126 | -h show this help
127 |
128 | prompt keys:
129 | b toggle bc mode
130 | r show result from last operation
131 | s show sizes of storage types
132 | ? show prompt help
133 | q/double ↵ quit program
134 | ```
135 |
136 | #### Operational notes
137 |
138 | - **Interactive mode**: `bcal` enters the REPL mode if no arguments are provided. Storage unit conversion, base conversion and expression evaluation are supported in this mode. The last valid result is stored in the variable **r**.
139 | - **Expression**: Expression passed as argument in one-shot mode must be within double quotes. Inner spaces are ignored. Supported operators: `+`, `-`, `*`, `/`, `%` and C bitwise operators (except `~` due to storage width dependency).
140 | - **N [unit]**: `N` can be a decimal or '0x' prefixed hex value. `unit` can be B/KiB/MiB/GiB/TiB/kB/MB/GB/TB. Default is Byte. As all of these tokens are unique, `unit` is case-insensitive.
141 | - **Numeric representation**: Decimal and hex are recognized in expressions and unit conversions. Binary is also recognized in other operations.
142 | - **Syntax**: Prefix hex inputs with `0x`, binary inputs with `0b`.
143 | - **Precision**: 128 bits if `__uint128_t` is available or 64 bits for numerical conversions. Floating point operations use `long double`. Negative values in storage expressions are unsupported. Only 64-bit operating systems are supported.
144 | - **Fractional bytes do not exist** because they can't be addressed. `bcal` shows the floor value of non-integer _bytes_.
145 | - **CHS and LBA syntax**:
146 | - LBA: `lLBA-MAX_HEAD-MAX_SECTOR` [NOTE: LBA starts with `l` (case ignored)]
147 | - CHS: `cC-H-S-MAX_HEAD-MAX_SECTOR` [NOTE: CHS starts with `c` (case ignored)]
148 | - Format conversion arguments must be hyphen separated.
149 | - Any unspecified value, including the one preceding the first `-` to the one following the last `-`, is considered `0` (zero).
150 | - Examples:
151 | - `c-50--0x12-` -> C = 0, H = 50, S = 0, MH = 0x12, MS = 0
152 | - `l50-0x12` -> LBA = 50, MH = 0x12, MS = 0
153 | - **Default values**:
154 | - sector size: 0x200 (512)
155 | - max heads per cylinder: 0x10 (16)
156 | - max sectors per track: 0x3f (63)
157 | - **bc variables**: `scale` = 10, `ibase` = 10. `r` is synced and can be used in expressions. `bc` is not called in minimal output mode.
158 |
159 | ### Examples
160 |
161 | 1. Evaluate arithmetic expression of storage units.
162 |
163 | $ bcal "(5kb+2mb)/3"
164 | $ bcal "5 tb / 12"
165 | $ bcal "2.5mb*3"
166 | $ bcal "(2giB * 2) / (2kib >> 2)"
167 | 2. Convert storage capacity to other units and get address, LBA.
168 |
169 | $ bcal 20140115 b
170 | $ bcal 0x1335053 B
171 | $ bcal 0xaabbcc kb
172 | $ bcal 0xdef Gib
173 | Note that the units are case-insensitive.
174 | 3. Convert storage capacity, set sector size to 4096 to calculate LBA.
175 |
176 | $ bcal 0xaabbcc kb -s 4096
177 | 4. Convert LBA to CHS.
178 |
179 | $ bcal -f l500
180 | $ bcal -f l0x600-18-0x7e
181 | $ bcal -f l0x300-0x12-0x7e
182 | 5. Convert CHS to LBA.
183 |
184 | $ bcal -f c10-10-10
185 | $ bcal -f c0x10-0x10-0x10
186 | $ bcal -f c0x10-10-2-0x12
187 | $ bcal -f c-10-2-0x12
188 | $ bcal -f c0x10-10--0x12
189 | 6. Show binary, decimal and hex representations of a number.
190 |
191 | $ bcal -c 20140115
192 | $ bcal -c 0b1001100110101000001010011
193 | $ bcal -c 0x1335053
194 | bcal> c 20140115 // Interactive mode
195 | 7. Invoke `bc`.
196 |
197 | $ bcal -b '3.5 * 2.1 + 5.7'
198 | bcal> b // Interactive mode
199 | bc vars: scale = 10, ibase = 10, last = r
200 | bc> 3.5 * 2.1 + 5.7
201 | 8. Pipe input.
202 |
203 | $ printf '15 kib + 15 gib \n r / 5' | bcal -m
204 | $ printf '15 + 15 + 2' | bcal -bm
205 | 9. Redirect from file.
206 |
207 | $ cat expr
208 | 15 gib + 15 kib
209 | r / 5
210 | $ bcal -m < expr
211 | 10. Use as a general-purpose calculator.
212 |
213 | $ bcal -b
214 |
215 | ### Testing
216 |
217 | Due to the nature of the project, it's extremely important to test existing functionality before raising any PR. `bcal` has several test cases written in [`test.py`](test.py). To execute the test cases locally, install `pytest` and run:
218 |
219 | $ make
220 | $ python3 -m pytest test.py
221 |
222 | ### Copyright
223 |
224 | Copyright © 2016 [Arun Prakash Jana](https://github.com/jarun)
225 |
--------------------------------------------------------------------------------
/bcal.1:
--------------------------------------------------------------------------------
1 | .TH "BCAL" "1" "24 Jan 2022" "Version 2.4" "User Commands"
2 | .SH NAME
3 | bcal \- Storage expression calculator.
4 | .SH SYNOPSIS
5 | .B bcal [-c N] [-f loc] [-s bytes] [expr] [N [unit]] [-b [expr]] [-m] [-d] [-h]
6 | .SH DESCRIPTION
7 | .B bcal
8 | (Byte CALculator) is a command-line utility to help with numerical calculations and expressions involving binary prefixes, SI/IEC conversion, byte addressing, base conversion, LBA/CHS calculation etc.
9 | .PP
10 | It invokes GNU \fBbc\fR for non-storage expressions. Alternatively, it can also invoke \fBcalc\fR (\fIhttp://www.isthe.com/chongo/tech/comp/calc/\fR). To use \fBcalc\fR:
11 | .PP
12 | .EX
13 | .B export BCAL_USE_CALC=1
14 | .EE
15 | .PP
16 | \fBbcal\fR uses [SI and IEC binary prefixes]:
17 | .I https://en.wikipedia.org/wiki/Binary_prefix
18 | .PP
19 | .SH FEATURES
20 | .PP
21 | * evaluate arithmetic expressions involving storage units
22 | * perform general purpose calculations (using bc or calc)
23 | * works with piped input or file redirection
24 | * convert to IEC/SI standard data storage units
25 | * interactive mode with the last valid result stored for reuse
26 | * show the address in bytes
27 | * show address as LBA:OFFSET
28 | * convert CHS to LBA and vice versa
29 | * base conversion to binary, decimal and hex
30 | * custom sector size, max heads/cylinder and max sectors/track
31 | * minimal dependencies
32 | .SH OPERATIONAL NOTES
33 | .PP
34 | .IP 1. 4
35 | \fBInteractive mode\fR: \fBbcal\fR enters the REPL mode if no arguments are provided. Storage unit conversion, base conversion and expression evaluation are supported in this mode. The last valid result is stored in the variable \fBr\fR.
36 | .PP
37 | .IP 2. 4
38 | \fBExpression\fR: Expression passed as argument in one-shot mode must be within double quotes. Inner spaces are ignored. Supported operators: +, -, *, /, % and C bitwise operators (except ~ due to storage width dependency).
39 | .PP
40 | .IP 3. 4
41 | \fBN [unit]\fR: \fIN\fR can be a decimal or '0x' prefixed hex value. \fIunit\fR can be B/KiB/MiB/GiB/TiB/kB/MB/GB/TB. Default is Byte. As all of these tokens are unique, \fIunit\fR is case-insensitive.
42 | .PP
43 | .IP 4. 4
44 | \fBNumeric representation\fR: Decimal and hex are recognized in expressions and unit conversions. Binary is also recognized in other operations.
45 | .PP
46 | .IP 5. 4
47 | \fBSyntax\fR: Prefix hex inputs with '0x', binary inputs with '0b'.
48 | .PP
49 | .IP 6. 4
50 | \fBPrecision\fR: 128 bits if \fI__uint128_t\fR is available or 64 bits for numerical conversions. Floating point operations use \fIlong double\fR. Negative values in storage expressions are unsupported. Only 64-bit operating systems are supported.
51 | .PP
52 | .IP 7. 4
53 | \fBFractional bytes do not exist\fR, because they can't be addressed. \fBbcal\fR shows the floor value of non-integer \fIbytes\fR.
54 | .PP
55 | .IP 8. 4
56 | \fBCHS and LBA syntax\fR:
57 | - LBA: 'lLBA-MAX_HEAD-MAX_SECTOR' [NOTE: LBA starts with 'l' (case ignored)]
58 | - CHS: 'cC-H-S-MAX_HEAD-MAX_SECTOR' [NOTE: CHS starts with 'c' (case ignored)]
59 | - Format conversion arguments must be hyphen separated.
60 | - Any unspecified value, including the one preceding the first '-' to the one following the last '-', is considered '0' (zero).
61 | - Examples:
62 | - 'c-50--0x12-' -> C = 0, H = 50, S = 0, MH = 0x12, MS = 0
63 | - 'l50-0x12' -> LBA = 50, MH = 0x12, MS = 0
64 | .PP
65 | .IP 9. 4
66 | \fBDefault values\fR:
67 | - sector size: 0x200 (512)
68 | - max heads per cylinder: 0x10 (16)
69 | - max sectors per track: 0x3f (63)
70 | .PP
71 | .IP 10. 4
72 | \fBbc variables\fR: \fIscale\fR = 10, \fIibase\fR = 10. \fBr\fR is synced and can be used in expressions. \fBbc\fR is not called in minimal output mode. To use \fBcalc\fR instead of \fBbc\fR, \fIexport BCAL_USE_CALC=1\fR.
73 | .SH OPTIONS
74 | .TP
75 | .BI "-c=" N
76 | Show decimal, binary and hex representation of positive integer \fIN\fR.
77 | .TP
78 | .BI "-f=" loc
79 | Convert CHS to LBA or LBA to CHS. \fIloc\fR is hyphen-separated representation of LBA or CHS. Please refer to the \fBOperational Notes\fR section for more details.
80 | .TP
81 | .BI "-s=" bytes
82 | Sector size in bytes. Default value is 512.
83 | .TP
84 | .BI "-b=" [expr]
85 | Start in \fBbc\fR mode. If expression is provided, evaluate in \fBbc\fR and quit.
86 | .TP
87 | .BI "-m"
88 | Show minimal output (e.g. decimal bytes).
89 | .TP
90 | .BI "-d"
91 | Enable debug information and logs.
92 | .TP
93 | .BI "-h"
94 | Show program help, storage sizes on the system and exit.
95 | .SH PROMPT KEYS
96 | .TP
97 | .BI "b"
98 | Toggle \fBbc\fR mode.
99 | .TP
100 | .BI "r"
101 | Show result from last operation.
102 | .TP
103 | .BI "s"
104 | Show sizes of storage types.
105 | .TP
106 | .BI "?"
107 | Show prompt help.
108 | .TP
109 | .BI "q, double Enter"
110 | Quit the program.
111 | .SH EXAMPLES
112 | .PP
113 | .IP 1. 4
114 | Evaluate arithmetic expression of storage units
115 | .PP
116 | .EX
117 | .IP
118 | .B $ bcal """(5kb+2mb)/3"""
119 | .B $ bcal """5 tb / 12"""
120 | .B $ bcal """2.5mb*3"""
121 | .B $ bcal """(2giB * 2) / (2kib >> 2)"""
122 | .EE
123 | .PP
124 | .IP 2. 4
125 | Convert storage capacity to other units and get address, LBA.
126 | .PP
127 | .EX
128 | .IP
129 | .B $ bcal 20140115 b
130 | .B $ bcal 0x1335053 B
131 | .B $ bcal 0xaabbcc kb
132 | .B $ bcal 0xdef Gib
133 | Note that the units are case-insensitive.
134 | .EE
135 | .PP
136 | .IP 3. 4
137 | Convert storage capacity, set sector size to 4096 to calculate LBA.
138 | .PP
139 | .EX
140 | .IP
141 | .B $ bcal 0xaabbcc kb -s 4096
142 | .EE
143 | .PP
144 | .IP 4. 4
145 | Convert LBA to CHS.
146 | .PP
147 | .EX
148 | .IP
149 | .B $ bcal -f l500
150 | .B $ bcal -f l0x600-18-0x7e
151 | .B $ bcal -f l0x300-0x12-0x7e
152 | .EE
153 | .PP
154 | .IP 5. 4
155 | Convert CHS to LBA.
156 | .PP
157 | .EX
158 | .IP
159 | .B $ bcal -f c10-10-10
160 | .B $ bcal -f c0x10-0x10-0x10
161 | .B $ bcal -f c0x10-10-2-0x12
162 | .B $ bcal -f c-10-2-0x12
163 | .B $ bcal -f c0x10-10--0x12
164 | .EE
165 | .PP
166 | .IP 6. 4
167 | Show binary, decimal and hex representations of a number.
168 | .PP
169 | .EX
170 | .IP
171 | .B $ bcal -c 20140115
172 | .B $ bcal -c 0b1001100110101000001010011
173 | .B $ bcal -c 0x1335053
174 | .B bcal> c 20140115 // Interactive mode
175 | .EE
176 | .PP
177 | .IP 7. 4
178 | Invoke \fIbc\fR.
179 | .PP
180 | .EX
181 | .IP
182 | .B $ bcal -b '3.5 * 2.1 + 5.7'
183 | .B bcal> b // Interactive mode
184 | .B bc vars: scale = 10, ibase = 10, last = r
185 | .B bc> 3.5 * 2.1 + 5.7
186 | .EE
187 | .PP
188 | .IP 8. 4
189 | Pipe input.
190 | .PP
191 | .EX
192 | .IP
193 | .B $ printf '15 kib + 15 gib \en r / 5' | bcal -m
194 | .B $ printf '15 + 15 + 2' | bcal -bm
195 | .EE
196 | .PP
197 | .IP 9. 4
198 | Redirect from file.
199 | .PP
200 | .EX
201 | .IP
202 | .B $ cat expr
203 | .B 15 gib + 15 kib
204 | .B r / 5
205 | .B $ bcal -m < expr
206 | .EE
207 | .PP
208 | .IP 10. 4
209 | Use as a general-purpose calculator.
210 | .PP
211 | .EX
212 | .IP
213 | .B $ bcal -b
214 | .EE
215 | .SH AUTHORS
216 | Arun Prakash Jana
217 | .SH HOME
218 | .I https://github.com/jarun/bcal
219 | .SH REPORTING BUGS
220 | .I https://github.com/jarun/bcal/issues
221 | .SH LICENSE
222 | Copyright \(co 2016 Arun Prakash Jana
223 | .PP
224 | License GPLv3+: GNU GPL version 3 or later .
225 | .br
226 | This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
227 |
--------------------------------------------------------------------------------
/inc/dslib.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #define NUM_LEN 63
8 |
9 | typedef struct data {
10 | char p[NUM_LEN];
11 | char unit;
12 | } Data;
13 |
14 | typedef struct stack {
15 | Data d;
16 | struct stack *link;
17 | } stack;
18 |
19 | typedef struct queue {
20 | Data d;
21 | struct queue *link;
22 | } queue;
23 |
24 | static void push(stack **top, Data d)
25 | {
26 | stack *new = (stack *)malloc(sizeof(stack));
27 |
28 | new->d = d;
29 | new->link = NULL;
30 |
31 | if (*top == NULL)
32 | *top = new;
33 | else {
34 | new->link = *top;
35 | *top = new;
36 | }
37 | }
38 |
39 | static void pop(stack **top, Data *d)
40 | {
41 | d->p[0] = '\0';
42 | d->unit = 0;
43 |
44 | if (*top != NULL) {
45 | stack *tmp = *top;
46 | *top = (*top)->link;
47 | *d = tmp->d;
48 | free(tmp);
49 | }
50 | }
51 |
52 | static void enqueue(queue **front, queue **rear, Data d)
53 | {
54 | queue *new = (queue *)malloc(sizeof(queue));
55 |
56 | new->d = d;
57 | new->link = NULL;
58 |
59 | if (*front == NULL)
60 | *front = *rear = new;
61 | else {
62 | (*rear)->link = new;
63 | *rear = new;
64 | }
65 | }
66 |
67 | static void dequeue(queue **front, queue **rear, Data *d)
68 | {
69 | d->p[0] = '\0';
70 | d->unit = 0;
71 |
72 | if (*front != NULL) {
73 | queue *tmp;
74 |
75 | if (*front == *rear) {
76 | tmp = *front;
77 | *d = tmp->d;
78 | free(tmp);
79 | *front = *rear = NULL;
80 | } else {
81 | tmp = *front;
82 | *front = (*front)->link;
83 | *d = tmp->d;
84 | free(tmp);
85 | }
86 | }
87 | }
88 |
89 | static int isempty(stack *top)
90 | {
91 | if (top == NULL)
92 | return 1;
93 |
94 | return 0;
95 | }
96 |
97 | static char *top(stack *top)
98 | {
99 | if (top == NULL)
100 | return NULL;
101 |
102 | return top->d.p;
103 | }
104 |
105 | static void emptystack(stack **top)
106 | {
107 | stack *tmp;
108 |
109 | while (*top != NULL) {
110 | tmp = *top;
111 | *top = (*top)->link;
112 | free(tmp);
113 | }
114 | }
115 |
116 | static void cleanqueue(queue **front)
117 | {
118 | queue *tmp;
119 |
120 | while (*front != NULL) {
121 | tmp = *front;
122 | *front = (*front)->link;
123 | free(tmp);
124 | }
125 | }
126 |
127 | /*
128 | static void printstack(stack *top)
129 | {
130 | stack *i;
131 |
132 | printf("\nStack: ");
133 |
134 | if (top == NULL)
135 | printf("Empty");
136 |
137 | for (i = top; i != NULL; i = i->link)
138 | printf(" %s ", i->d.p);
139 |
140 | printf("\n");
141 | }
142 |
143 | static void printqueue(queue *front)
144 | {
145 | queue *i;
146 |
147 | printf("\nQueue: ");
148 |
149 | if (front == NULL)
150 | printf("Empty");
151 |
152 | for (i = front; i != NULL; i = i->link)
153 | printf(" %s ", i->d.p);
154 |
155 | printf("\n");
156 | }
157 | */
158 |
--------------------------------------------------------------------------------
/inc/log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * A modular, level based log file implementation
3 | *
4 | * Author: Arun Prakash Jana
5 | * Copyright (C) 2014 by Arun Prakash Jana
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with bcal. If not, see .
19 | */
20 |
21 | #pragma once
22 |
23 | #include
24 | #include
25 |
26 | #define ERROR 0
27 | #define WARNING 1
28 | #define INFO 2
29 | #define DEBUG 3
30 |
31 | #define log(level, format, ...) \
32 | debug_log(__func__, level, format, ##__VA_ARGS__)
33 |
34 | static void debug_log(const char *func, int level, const char *format, ...) __attribute__((__format__(printf, 3, 4)));
35 |
--------------------------------------------------------------------------------
/packagecore.yaml:
--------------------------------------------------------------------------------
1 | name: bcal
2 | maintainer: Arun Prakash Jana
3 | license: GPLv3
4 | summary: Storage expression calculator.
5 | homepage: https://github.com/jarun/bcal
6 | commands:
7 | install:
8 | - make PREFIX="/usr" install DESTDIR="${BP_DESTDIR}"
9 | packages:
10 | archlinux:
11 | builddeps:
12 | - make
13 | - gcc
14 | deps:
15 | - readline
16 | container: "archlinux/base"
17 | centos7.5:
18 | builddeps:
19 | - make
20 | - gcc
21 | - readline-devel
22 | deps:
23 | - readline
24 | centos7.6:
25 | builddeps:
26 | - make
27 | - gcc
28 | - readline-devel
29 | deps:
30 | - readline
31 | centos8.0:
32 | builddeps:
33 | - make
34 | - gcc
35 | - readline-devel
36 | deps:
37 | - readline
38 | debian9:
39 | builddeps:
40 | - make
41 | - gcc
42 | - libreadline-dev
43 | deps:
44 | - readline-common
45 | debian10:
46 | builddeps:
47 | - make
48 | - gcc
49 | - libreadline-dev
50 | deps:
51 | - readline-common
52 | fedora29:
53 | builddeps:
54 | - make
55 | - gcc
56 | - readline-devel
57 | deps:
58 | - readline
59 | fedora30:
60 | builddeps:
61 | - make
62 | - gcc
63 | - readline-devel
64 | deps:
65 | - readline
66 | fedora31:
67 | builddeps:
68 | - make
69 | - gcc
70 | - readline-devel
71 | deps:
72 | - readline
73 | opensuse15.1:
74 | builddeps:
75 | - make
76 | - gcc
77 | - readline-devel
78 | deps:
79 | - readline
80 | ubuntu16.04:
81 | builddeps:
82 | - make
83 | - gcc
84 | - libreadline6-dev
85 | deps:
86 | - libreadline6
87 | ubuntu18.04:
88 | builddeps:
89 | - make
90 | - gcc
91 | - libreadline-dev
92 | deps:
93 | - libreadline7
94 |
--------------------------------------------------------------------------------
/src/bcal.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Byte CALculator
3 | *
4 | * Author: Arun Prakash Jana
5 | * Copyright (C) 2016 by Arun Prakash Jana
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with bcal. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include "dslib.h"
34 | #include "log.h"
35 |
36 | #define SECTOR_SIZE 512 /* 0x200 */
37 | #define MAX_HEAD 16 /* 0x10 */
38 | #define MAX_SECTOR 63 /* 0x3f */
39 | #define UINT_BUF_LEN 40 /* log10(1 << 128) + '\0' */
40 | #define FLOAT_BUF_LEN 128
41 | #define FLOAT_WIDTH 40
42 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
43 | #define MAX_BITS 128
44 | #define ALIGNMENT_MASK_4BIT 0xF
45 | #define ELEMENTS(x) (sizeof(x) / sizeof(*(x)))
46 |
47 | typedef unsigned char uchar;
48 | typedef unsigned int uint;
49 | typedef unsigned long ulong;
50 | typedef unsigned long long ull;
51 | typedef long double maxfloat_t;
52 |
53 | #ifdef __SIZEOF_INT128__
54 | typedef __uint128_t maxuint_t;
55 | #else
56 | typedef __uint64_t maxuint_t;
57 | #endif
58 |
59 | /* CHS representation */
60 | typedef struct {
61 | ulong c;
62 | ulong h;
63 | ulong s;
64 | } t_chs;
65 |
66 | /* Settings */
67 | typedef struct {
68 | uchar bcmode : 1;
69 | uchar minimal : 1;
70 | uchar repl : 1;
71 | uchar calc : 1;
72 | uchar rsvd : 2; /* Reserved for future usage */
73 | uchar loglvl : 2;
74 | } settings;
75 |
76 | static char *VERSION = "2.4";
77 | static char *units[] = {"b", "kib", "mib", "gib", "tib", "kb", "mb", "gb", "tb"};
78 | static char *logarr[] = {"ERROR", "WARNING", "INFO", "DEBUG"};
79 |
80 | static char *FAILED = "1";
81 | static char *PASSED = "\0";
82 | static char *curexpr = NULL;
83 | static char prompt[8] = "bcal> ";
84 |
85 | static char uint_buf[UINT_BUF_LEN];
86 | static char float_buf[FLOAT_BUF_LEN];
87 |
88 | static Data lastres = {"\0", 0};
89 | static settings cfg = {0, 0, 0, 0, 0, INFO};
90 |
91 | static const char* const error_strings[] = {
92 | "is undefined",
93 | "Missing operator"
94 | };
95 |
96 | static void debug_log(const char *func, int level, const char *format, ...)
97 | {
98 | va_list ap;
99 |
100 | if (level < 0 || level > DEBUG)
101 | return;
102 |
103 | va_start(ap, format);
104 |
105 | if (level <= cfg.loglvl) {
106 | if (cfg.loglvl == DEBUG) {
107 | fprintf(stderr, "%s(), %s: ", func, logarr[level]);
108 | vfprintf(stderr, format, ap);
109 | } else {
110 | fprintf(stderr, "%s: ", logarr[level]);
111 | vfprintf(stderr, format, ap);
112 | }
113 | }
114 |
115 | va_end(ap);
116 | }
117 |
118 | /*
119 | * Just a safe strncpy(3)
120 | * Always null ('\0') terminates if both src and dest are valid pointers.
121 | * Returns the number of bytes copied including terminating null byte.
122 | */
123 | static size_t bstrlcpy(char *dest, const char *src, size_t n)
124 | {
125 | static ulong *s, *d;
126 | static size_t len, blocks;
127 | static const uint lsize = sizeof(ulong);
128 | static const uint WORD_SHIFT = (sizeof(ulong) == 8) ? 3 : 2;
129 |
130 | if (!src || !dest || !n)
131 | return 0;
132 |
133 | len = strlen(src) + 1;
134 | if (n > len)
135 | n = len;
136 | else if (len > n)
137 | /* Save total number of bytes to copy in len */
138 | len = n;
139 |
140 | /*
141 | * To enable -O3 ensure src and dest are 16-byte aligned
142 | * More info: http://www.felixcloutier.com/x86/MOVDQA.html
143 | */
144 | if ((n >= lsize) && (((ulong)src & ALIGNMENT_MASK_4BIT) == 0
145 | && ((ulong)dest & ALIGNMENT_MASK_4BIT) == 0)) {
146 | s = (ulong *)src;
147 | d = (ulong *)dest;
148 | blocks = n >> WORD_SHIFT;
149 | n &= lsize - 1;
150 |
151 | while (blocks) {
152 | *d = *s;
153 | ++d, ++s;
154 | --blocks;
155 | }
156 |
157 | if (!n) {
158 | dest = (char *)d;
159 | *--dest = '\0';
160 | return len;
161 | }
162 |
163 | src = (char *)s;
164 | dest = (char *)d;
165 | }
166 |
167 | while (--n && (*dest = *src))
168 | ++dest, ++src;
169 |
170 | if (!n)
171 | *dest = '\0';
172 |
173 | return len;
174 | }
175 |
176 | static bool program_exit(const char *str)
177 | {
178 | if (!strcmp(str, "exit") || !strcmp(str, "quit"))
179 | return true;
180 | return false;
181 | }
182 |
183 | static void remove_commas(char *str)
184 | {
185 | if (!str || !*str)
186 | return;
187 |
188 | char *iter1, *iter2;
189 |
190 | for (iter1 = iter2 = str; *iter2; iter2++)
191 | if (*iter2 != ',')
192 | *iter1++ = *iter2;
193 |
194 | *iter1 = '\0';
195 | }
196 |
197 | /*
198 | * Try to evaluate en expression using bc
199 | * If argument is NULL, global curexpr is picked
200 | */
201 | static int try_bc(char *expr)
202 | {
203 | pid_t pid;
204 | int pipe_pc[2], pipe_cp[2];
205 | size_t len;
206 | ssize_t ret;
207 | char *ptr = cfg.calc ? "calc" : "bc";
208 |
209 | remove_commas(expr);
210 |
211 | if (!expr) {
212 | if (curexpr)
213 | expr = curexpr;
214 | else
215 | return -1;
216 | }
217 |
218 | log(DEBUG, "expression: \"%s\"\n", expr);
219 |
220 | if (program_exit(expr))
221 | exit(0);
222 |
223 | if (pipe(pipe_pc) == -1 || pipe(pipe_cp) == -1) {
224 | log(ERROR, "pipe()!\n");
225 | exit(EXIT_FAILURE);
226 | }
227 |
228 | pid = fork();
229 |
230 | if (pid == -1) {
231 | log(ERROR, "fork() failed!\n");
232 | return -1;
233 | }
234 |
235 | if (pid == 0) { /* child */
236 | close(STDOUT_FILENO);
237 | close(STDIN_FILENO);
238 | close(pipe_pc[1]); // Close writing end
239 | close(pipe_cp[0]); // Close reading end
240 |
241 | dup2(pipe_pc[0], STDIN_FILENO); // Take stdin from parent
242 | dup2(pipe_cp[1], STDOUT_FILENO); // Give stdout to parent
243 | dup2(pipe_cp[1], STDERR_FILENO); // Give stderr to parent
244 |
245 | int ret = execlp(ptr, ptr, (char*) NULL);
246 | log(ERROR, "execlp() failed!\n");
247 | exit(ret);
248 | }
249 |
250 | if (!cfg.calc) {
251 | /* parent */
252 | if (write(pipe_pc[1], "scale=10\n", 9) != 9) {
253 | log(ERROR, "write(1)! [%s]\n", strerror(errno));
254 | exit(-1);
255 | }
256 | }
257 |
258 | if (write(pipe_pc[1], "r=", 2) != 2) {
259 | log(ERROR, "write(2)! [%s]\n", strerror(errno));
260 | exit(-1);
261 | }
262 |
263 | if (lastres.p[0]) {
264 | ret = (ssize_t)strlen(lastres.p);
265 | if (write(pipe_pc[1], lastres.p, ret) != ret) {
266 | log(ERROR, "write(3)! [%s]\n", strerror(errno));
267 | exit(-1);
268 | }
269 | } else {
270 | if (write(pipe_pc[1], "0", 1) != 1) {
271 | log(ERROR, "write(4)! [%s]\n", strerror(errno));
272 | exit(-1);
273 | }
274 | }
275 |
276 | if (write(pipe_pc[1], "\n", 1) != 1) {
277 | log(ERROR, "write(5)! [%s]\n", strerror(errno));
278 | exit(-1);
279 | }
280 |
281 | ret = (ssize_t)strlen(expr);
282 | if (write(pipe_pc[1], expr, ret) != ret) {
283 | log(ERROR, "write(6)! [%s]\n", strerror(errno));
284 | exit(-1);
285 | }
286 |
287 | if (write(pipe_pc[1], "\n", 1) != 1) {
288 | log(ERROR, "write(7)! [%s]\n", strerror(errno));
289 | exit(-1);
290 | }
291 |
292 | static char buffer[128];
293 |
294 | ret = read(pipe_cp[0], buffer, sizeof(buffer) - 1);
295 | if (ret == -1) {
296 | log(ERROR, "read()! [%s]\n", strerror(errno));
297 | exit(-1);
298 | }
299 |
300 | if (write(pipe_pc[1], "quit\n", 5) != 5) {
301 | log(ERROR, "write(7)! [%s]\n", strerror(errno));
302 | exit(-1);
303 | }
304 |
305 | if (cfg.calc)
306 | kill(pid, SIGTERM);
307 |
308 | close(pipe_cp[0]);
309 | close(pipe_pc[1]);
310 |
311 | buffer[ret] = '\0';
312 |
313 | if ((buffer[0] != '(') && (strncmp(buffer, "Warning", 7) != 0) && (strncmp(buffer, "Missing", 7) != 0)) {
314 | ptr = buffer;
315 | while (isspace(*ptr)) /* calc results have space before them */
316 | ++ptr;
317 |
318 | printf("%s", ptr); /* Print the result/error */
319 |
320 | /* Detect common error conditions for calc and stop */
321 | if (cfg.calc)
322 | for (size_t r = 0; r < ELEMENTS(error_strings); ++r)
323 | if (strstr(ptr, error_strings[r]))
324 | return -1;
325 |
326 | /* Store the result in 'r' for next usage */
327 | len = bstrlcpy(lastres.p, ptr, NUM_LEN);
328 |
329 | /* remove newline appended at the end of result by bc */
330 | (len >= 2) ? (len -= 2) : (len = 0);
331 | lastres.p[len] = '\0';
332 |
333 | #ifdef TRIM_DECIMAL
334 | /* Trim the decimal part, if any */
335 | size_t count = 0;
336 |
337 | while (count < len) {
338 | if (lastres.p[count] == '.') {
339 | lastres.p[count] = '\0';
340 | break;
341 | }
342 |
343 | ++count;
344 | }
345 | #endif
346 | lastres.unit = 0;
347 | log(DEBUG, "result: %s %d\n", lastres.p, lastres.unit);
348 | return 0;
349 | }
350 |
351 | log(ERROR, "invalid expression\n");
352 | return -1;
353 | }
354 |
355 | static void binprint(maxuint_t n)
356 | {
357 | int count = MAX_BITS - 1;
358 | int pos = MAX_BITS + (MAX_BITS >> 2) - 1;
359 | char binstr[MAX_BITS + (MAX_BITS >> 2) + 1] = {0};
360 |
361 | if (!n) {
362 | printf("0");
363 | return;
364 | }
365 |
366 | while (n && count >= 0) {
367 | binstr[pos] = "01"[n & 1];
368 | --pos;
369 | n >>= 1;
370 | if (n && count && !(count & 7)) {
371 | binstr[pos] = ' ';
372 | --pos;
373 | }
374 | --count;
375 | }
376 |
377 | ++pos;
378 |
379 | printf("%s", binstr + pos);
380 | }
381 |
382 | static char *getstr_u128(maxuint_t n, char *buf)
383 | {
384 | if (n == 0) {
385 | buf[0] = '0';
386 | buf[1] = '\0';
387 | return buf;
388 | }
389 |
390 | memset(buf, 0, UINT_BUF_LEN);
391 | char *loc = buf + UINT_BUF_LEN - 1; /* start at the end */
392 |
393 | while (n != 0) {
394 | if (loc == buf)
395 | return NULL; /* should not happen */
396 |
397 | *--loc = "0123456789"[n % 10]; /* save the last digit */
398 | n /= 10; /* drop the last digit */
399 | }
400 |
401 | return loc;
402 | }
403 |
404 | static char *getstr_f128(maxfloat_t val, char *buf)
405 | {
406 | int n = snprintf(buf, FLOAT_BUF_LEN, "%#*.10Le", FLOAT_WIDTH, val);
407 |
408 | buf[n] = '\0';
409 | return buf;
410 | }
411 |
412 | static void printval(maxfloat_t val, char *unit)
413 | {
414 | if (val - (maxuint_t)val == 0) // NOLINT
415 | printf("%40s %s\n", getstr_u128((maxuint_t)val, uint_buf), unit);
416 | else
417 | printf("%s %s\n", getstr_f128(val, float_buf), unit);
418 | }
419 |
420 | static void printhex_u128(maxuint_t n)
421 | {
422 | ull high = (ull)(n >> (sizeof(maxuint_t) << 2));
423 |
424 | if (high)
425 | printf("0x%llx%llx", high, (ull)n);
426 | else
427 | printf("0x%llx", (ull)n);
428 | }
429 |
430 | /* This function adds check for binary input to strtoul() */
431 | static ulong strtoul_b(char *token)
432 | {
433 | int base = 0;
434 |
435 | /* NOTE: no NULL check here! */
436 |
437 | if (strlen(token) > 2 && token[0] == '0' &&
438 | (token[1] == 'b' || token[1] == 'B')) {
439 | base = 2;
440 | }
441 |
442 | return strtoul(token + base, NULL, base);
443 | }
444 |
445 | /* This function adds check for binary input to strtoull() */
446 | static ull strtoull_b(char *token)
447 | {
448 | int base = 0;
449 |
450 | /* NOTE: no NULL check here! */
451 |
452 | if (strlen(token) > 2 && token[0] == '0' &&
453 | (token[1] == 'b' || token[1] == 'B')) {
454 | base = 2;
455 | }
456 |
457 | return strtoull(token + base, NULL, base);
458 | }
459 |
460 | /* Converts a char to unsigned int according to base */
461 | static bool ischarvalid(char ch, uint base, uint *val)
462 | {
463 | if (base == 2)
464 | {
465 | if (ch == '0' || ch == '1') {
466 | *val = ch - '0';
467 | return true;
468 | }
469 | } else if (base == 16) {
470 | if (ch >= '0' && ch <= '9') {
471 | *val = ch - '0';
472 | return true;
473 | }
474 |
475 | if (ch >= 'a' && ch <= 'f') {
476 | *val = (ch - 'a') + 10;
477 | return true;
478 | }
479 |
480 | if (ch >= 'A' && ch <= 'F') {
481 | *val = (ch - 'A') + 10;
482 | return true;
483 | }
484 | } else if (base == 10) {
485 | if (ch >= '0' && ch <= '9') {
486 | *val = ch - '0';
487 | return true;
488 | }
489 | }
490 |
491 | return false;
492 | }
493 |
494 | /*
495 | * Converts a non-floating representing string to maxuint_t
496 | */
497 | static maxuint_t strtouquad(char *token, char **pch)
498 | {
499 | *pch = PASSED;
500 |
501 | if (!token || !*token) {
502 | *pch = FAILED;
503 | return 0;
504 | }
505 |
506 | char *ptr;
507 | maxuint_t val = 0, prevval = 0;
508 | uint base = 10, multiplier = 0, digit, bits_used = 0;
509 | uint max_bit_len = sizeof(maxuint_t) << 3;
510 |
511 | if (token[0] == '0') {
512 | if (token[1] == 'b' || token[1] == 'B') { /* binary */
513 | base = 2;
514 | multiplier = 1;
515 | } else if (token[1] == 'x' || token[1] == 'X') { /* hex */
516 | base = 16;
517 | multiplier = 4;
518 | }
519 | }
520 |
521 | if (base == 2 || base == 16) {
522 | ptr = token + 2;
523 |
524 | if (!*ptr) {
525 | *pch = FAILED;
526 | return 0;
527 | }
528 |
529 | while (*ptr && *ptr == '0')
530 | ++ptr;
531 |
532 | if (!*ptr)
533 | return 0;
534 |
535 | while (*ptr) {
536 | if (bits_used == max_bit_len || !ischarvalid(*ptr, base, &digit)) {
537 | *pch = FAILED;
538 | return 0;
539 | }
540 |
541 | val = (val << multiplier) + digit;
542 |
543 | ++bits_used;
544 | ++ptr;
545 | }
546 |
547 | return val;
548 | }
549 |
550 | /* Try base 10 for any other pattern */
551 | ptr = token;
552 | while (*ptr && *ptr == '0')
553 | ++ptr;
554 |
555 | if (!*ptr)
556 | return 0;
557 |
558 | while (*ptr) {
559 | if (!ischarvalid(*ptr, base, &digit)) {
560 | *pch = FAILED;
561 | return 0;
562 | }
563 |
564 | val = (val * 10) + digit;
565 |
566 | /* Try to to detect overflow */
567 | if (val < prevval) {
568 | *pch = FAILED;
569 | return 0;
570 | }
571 |
572 | prevval = val;
573 | ++ptr;
574 | }
575 |
576 | return val;
577 | }
578 |
579 | static maxuint_t convertbyte(char *buf, int *ret)
580 | {
581 | maxfloat_t val;
582 | char *pch;
583 | /* Convert and print in bytes (cannot be in float) */
584 | maxuint_t bytes = strtouquad(buf, &pch);
585 | if (*pch) {
586 | *ret = -1;
587 | return 0;
588 | }
589 |
590 | *ret = 0;
591 |
592 | if (cfg.minimal) {
593 | printf("%s B\n", getstr_u128(bytes, uint_buf));
594 | return bytes;
595 | }
596 |
597 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
598 |
599 | /* Convert and print in IEC standard units */
600 |
601 | printf("\n IEC standard (base 2)\n\n");
602 | val = (maxfloat_t)bytes / 1024;
603 | printval(val, "KiB");
604 |
605 | val = (maxfloat_t)bytes / (1 << 20);
606 | printval(val, "MiB");
607 |
608 | val = (maxfloat_t)bytes / (1 << 30);
609 | printval(val, "GiB");
610 |
611 | val = (maxfloat_t)bytes / ((unsigned long long)1 << 40);
612 | printval(val, "TiB");
613 |
614 | /* Convert and print in SI standard values */
615 |
616 | printf("\n SI standard (base 10)\n\n");
617 | val = (maxfloat_t)bytes / 1000;
618 | printval(val, "kB");
619 |
620 | val = (maxfloat_t)bytes / 1000000;
621 | printval(val, "MB");
622 |
623 | val = (maxfloat_t)bytes / 1000000000;
624 | printval(val, "GB");
625 |
626 | val = (maxfloat_t)bytes / 1000000000000;
627 | printval(val, "TB");
628 |
629 | return bytes;
630 | }
631 |
632 | static maxuint_t convertkib(char *buf, int *ret)
633 | {
634 | char *pch;
635 | maxfloat_t val, kib = strtold(buf, &pch);
636 | if (*pch) {
637 | *ret = -1;
638 | return 0;
639 | }
640 |
641 | *ret = 0;
642 |
643 | maxuint_t bytes = (maxuint_t)(kib * 1024);
644 |
645 | if (cfg.minimal) {
646 | printf("%s B\n", getstr_u128(bytes, uint_buf));
647 | return bytes;
648 | }
649 |
650 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
651 |
652 | printf("\n IEC standard (base 2)\n\n");
653 | printval(kib, "KiB");
654 |
655 | val = kib / 1024;
656 | printval(val, "MiB");
657 |
658 | val = kib / (1 << 20);
659 | printval(val, "GiB");
660 |
661 | val = kib / (1 << 30);
662 | printval(val, "TiB");
663 |
664 | printf("\n SI standard (base 10)\n\n");
665 | val = kib * 1024 / 1000;
666 | printval(val, "kB");
667 |
668 | val = kib * 1024 / 1000000;
669 | printval(val, "MB");
670 |
671 | val = kib * 1024 / 1000000000;
672 | printval(val, "GB");
673 |
674 | val = kib * 1024 / 1000000000000;
675 | printval(val, "TB");
676 |
677 | return bytes;
678 | }
679 |
680 | static maxuint_t convertmib(char *buf, int *ret)
681 | {
682 | char *pch;
683 | maxfloat_t val, mib = strtold(buf, &pch);
684 | if (*pch) {
685 | *ret = -1;
686 | return 0;
687 | }
688 |
689 | *ret = 0;
690 |
691 | maxuint_t bytes = (maxuint_t)(mib * (1 << 20));
692 |
693 | if (cfg.minimal) {
694 | printf("%s B\n", getstr_u128(bytes, uint_buf));
695 | return bytes;
696 | }
697 |
698 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
699 |
700 | printf("\n IEC standard (base 2)\n\n");
701 | val = mib * 1024;
702 | printval(val, "KiB");
703 |
704 | printval(mib, "MiB");
705 |
706 | val = mib / 1024;
707 | printval(val, "GiB");
708 |
709 | val = mib / (1 << 20);
710 | printval(val, "TiB");
711 |
712 | printf("\n SI standard (base 10)\n\n");
713 | val = mib * (1 << 20) / 1000;
714 | printval(val, "kB");
715 |
716 | val = mib * (1 << 20) / 1000000;
717 | printval(val, "MB");
718 |
719 | val = mib * (1 << 20) / 1000000000;
720 | printval(val, "GB");
721 |
722 | val = mib * (1 << 20) / 1000000000000;
723 | printval(val, "TB");
724 |
725 | return bytes;
726 | }
727 |
728 | static maxuint_t convertgib(char *buf, int *ret)
729 | {
730 | char *pch;
731 | maxfloat_t val, gib = strtold(buf, &pch);
732 | if (*pch) {
733 | *ret = -1;
734 | return 0;
735 | }
736 |
737 | *ret = 0;
738 |
739 | maxuint_t bytes = (maxuint_t)(gib * (1 << 30));
740 |
741 | if (cfg.minimal) {
742 | printf("%s B\n", getstr_u128(bytes, uint_buf));
743 | return bytes;
744 | }
745 |
746 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
747 |
748 | printf("\n IEC standard (base 2)\n\n");
749 | val = gib * (1 << 20);
750 | printval(val, "KiB");
751 |
752 | val = gib * 1024;
753 | printval(val, "MiB");
754 |
755 | printval(gib, "GiB");
756 |
757 | val = gib / 1024;
758 | printval(val, "TiB");
759 |
760 | printf("\n SI standard (base 10)\n\n");
761 | val = gib * (1 << 30) / 1000;
762 | printval(val, "kB");
763 |
764 | val = gib * (1 << 30) / 1000000;
765 | printval(val, "MB");
766 |
767 | val = gib * (1 << 30) / 1000000000;
768 | printval(val, "GB");
769 |
770 | val = gib * (1 << 30) / 1000000000000;
771 | printval(val, "TB");
772 |
773 | return bytes;
774 | }
775 |
776 | static maxuint_t converttib(char *buf, int *ret)
777 | {
778 | char *pch;
779 | maxfloat_t val, tib = strtold(buf, &pch);
780 | if (*pch) {
781 | *ret = -1;
782 | return 0;
783 | }
784 |
785 | *ret = 0;
786 |
787 | maxuint_t bytes = (maxuint_t)(tib * ((maxuint_t)1 << 40));
788 |
789 | if (cfg.minimal) {
790 | printf("%s B\n", getstr_u128(bytes, uint_buf));
791 | return bytes;
792 | }
793 |
794 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
795 |
796 | printf("\n IEC standard (base 2)\n\n");
797 | val = tib * (1 << 30);
798 | printval(val, "KiB");
799 |
800 | val = tib * (1 << 20);
801 | printval(val, "MiB");
802 |
803 | val = tib * 1024;
804 | printval(val, "GiB");
805 |
806 | printval(tib, "TiB");
807 |
808 | printf("\n SI standard (base 10)\n\n");
809 | val = tib * ((maxuint_t)1 << 40) / 1000;
810 | printval(val, "kB");
811 |
812 | val = tib * ((maxuint_t)1 << 40) / 1000000;
813 | printval(val, "MB");
814 |
815 | val = tib * ((maxuint_t)1 << 40) / 1000000000;
816 | printval(val, "GB");
817 |
818 | val = tib * ((maxuint_t)1 << 40) / 1000000000000;
819 | printval(val, "TB");
820 |
821 | return bytes;
822 | }
823 |
824 | static maxuint_t convertkb(char *buf, int *ret)
825 | {
826 | char *pch;
827 | maxfloat_t val, kb = strtold(buf, &pch);
828 | if (*pch) {
829 | *ret = -1;
830 | return 0;
831 | }
832 |
833 | *ret = 0;
834 |
835 | maxuint_t bytes = (maxuint_t)(kb * 1000);
836 |
837 | if (cfg.minimal) {
838 | printf("%s B\n", getstr_u128(bytes, uint_buf));
839 | return bytes;
840 | }
841 |
842 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
843 |
844 | printf("\n IEC standard (base 2)\n\n");
845 | val = kb * 1000 / 1024;
846 | printval(val, "KiB");
847 |
848 | val = kb * 1000 / (1 << 20);
849 | printval(val, "MiB");
850 |
851 | val = kb * 1000 / (1 << 30);
852 | printval(val, "GiB");
853 |
854 | val = kb * 1000 / ((maxuint_t)1 << 40);
855 | printval(val, "TiB");
856 |
857 | printf("\n SI standard (base 10)\n\n");
858 | printval(kb, "kB");
859 |
860 | val = kb / 1000;
861 | printval(val, "MB");
862 |
863 | val = kb / 1000000;
864 | printval(val, "GB");
865 |
866 | val = kb / 1000000000;
867 | printval(val, "TB");
868 |
869 | return bytes;
870 | }
871 |
872 | static maxuint_t convertmb(char *buf, int *ret)
873 | {
874 | char *pch;
875 | maxfloat_t val, mb = strtold(buf, &pch);
876 | if (*pch) {
877 | *ret = -1;
878 | return 0;
879 | }
880 |
881 | *ret = 0;
882 |
883 | maxuint_t bytes = (maxuint_t)(mb * 1000000);
884 |
885 | if (cfg.minimal) {
886 | printf("%s B\n", getstr_u128(bytes, uint_buf));
887 | return bytes;
888 | }
889 |
890 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
891 |
892 | printf("\n IEC standard (base 2)\n\n");
893 | val = mb * 1000000 / 1024;
894 | printval(val, "KiB");
895 |
896 | val = mb * 1000000 / (1 << 20);
897 | printval(val, "MiB");
898 |
899 | val = mb * 1000000 / (1 << 30);
900 | printval(val, "GiB");
901 |
902 | val = mb * 1000000 / ((maxuint_t)1 << 40);
903 | printval(val, "TiB");
904 |
905 | printf("\n SI standard (base 10)\n\n");
906 | val = mb * 1000;
907 | printval(val, "kB");
908 |
909 | printval(mb, "MB");
910 |
911 | val = mb / 1000;
912 | printval(val, "GB");
913 |
914 | val = mb / 1000000;
915 | printval(val, "TB");
916 |
917 | return bytes;
918 | }
919 |
920 | static maxuint_t convertgb(char *buf, int *ret)
921 | {
922 | char *pch;
923 | maxfloat_t val, gb = strtold(buf, &pch);
924 | if (*pch) {
925 | *ret = -1;
926 | return 0;
927 | }
928 |
929 | *ret = 0;
930 |
931 | maxuint_t bytes = (maxuint_t)(gb * 1000000000);
932 |
933 | if (cfg.minimal) {
934 | printf("%s B\n", getstr_u128(bytes, uint_buf));
935 | return bytes;
936 | }
937 |
938 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
939 |
940 | printf("\n IEC standard (base 2)\n\n");
941 | val = gb * 1000000000 / 1024;
942 | printval(val, "KiB");
943 |
944 | val = gb * 1000000000 / (1 << 20);
945 | printval(val, "MiB");
946 |
947 | val = gb * 1000000000 / (1 << 30);
948 | printval(val, "GiB");
949 |
950 | val = gb * 1000000000 / ((maxuint_t)1 << 40);
951 | printval(val, "TiB");
952 |
953 | printf("\n SI standard (base 10)\n\n");
954 | val = gb * 1000000;
955 | printval(val, "kB");
956 |
957 | val = gb * 1000;
958 | printval(val, "MB");
959 |
960 | printval(gb, "GB");
961 |
962 | val = gb / 1000;
963 | printval(val, "TB");
964 |
965 | return bytes;
966 | }
967 |
968 | static maxuint_t converttb(char *buf, int *ret)
969 | {
970 | char *pch;
971 | maxfloat_t val, tb = strtold(buf, &pch);
972 | if (*pch) {
973 | *ret = -1;
974 | return 0;
975 | }
976 |
977 | *ret = 0;
978 |
979 | maxuint_t bytes = (__uint128_t)(tb * 1000000000000);
980 |
981 | if (cfg.minimal) {
982 | printf("%s B\n", getstr_u128(bytes, uint_buf));
983 | return bytes;
984 | }
985 |
986 | printf("%40s B\n", getstr_u128(bytes, uint_buf));
987 |
988 | printf("\n IEC standard (base 2)\n\n");
989 | val = tb * 1000000000000 / 1024;
990 | printval(val, "KiB");
991 |
992 | val = tb * 1000000000000 / (1 << 20);
993 | printval(val, "MiB");
994 |
995 | val = tb * 1000000000000 / (1 << 30);
996 | printval(val, "GiB");
997 |
998 | val = tb * 1000000000000 / ((maxuint_t)1 << 40);
999 | printval(val, "TiB");
1000 |
1001 | printf("\n SI standard (base 10)\n\n");
1002 | val = tb * 1000000000;
1003 | printval(val, "kB");
1004 |
1005 | val = tb * 1000000;
1006 | printval(val, "MB");
1007 |
1008 | val = tb * 1000;
1009 | printval(val, "GB");
1010 |
1011 | printval(tb, "TB");
1012 |
1013 | return bytes;
1014 | }
1015 |
1016 | static bool chs2lba(char *chs, maxuint_t *lba)
1017 | {
1018 | int token_no = 0;
1019 | char *ptr, *token;
1020 | ulong param[5] = {0, 0, 0, MAX_HEAD, MAX_SECTOR};
1021 |
1022 | ptr = token = chs;
1023 |
1024 | while (*ptr && token_no < 5) {
1025 | if (*ptr == '-') {
1026 | /* Replace '-' with NULL and get the token */
1027 | *ptr = '\0';
1028 | param[token_no] = strtoul_b(token);
1029 | ++token_no;
1030 | /* Restore the '-' */
1031 | *ptr = '-';
1032 | ++ptr;
1033 | /* Point to start of next token */
1034 | token = ptr;
1035 |
1036 | if (*ptr == '\0' && token_no < 5) {
1037 | param[token_no] = strtoul_b(token);
1038 | ++token_no;
1039 | }
1040 |
1041 | continue;
1042 | }
1043 |
1044 | ++ptr;
1045 |
1046 | if (*ptr == '\0' && token_no < 5) {
1047 | param[token_no] = strtoul_b(token);
1048 | ++token_no;
1049 | }
1050 | }
1051 |
1052 | /* Fail if CHS is omitted */
1053 | if (token_no < 3) {
1054 | log(ERROR, "CHS missing\n");
1055 | return false;
1056 | }
1057 |
1058 | if (!param[3]) {
1059 | log(ERROR, "MAX_HEAD = 0\n");
1060 | return false;
1061 | }
1062 |
1063 | if (!param[4]) {
1064 | log(ERROR, "MAX_SECTOR = 0\n");
1065 | return false;
1066 | }
1067 |
1068 | if (!param[2]) {
1069 | log(ERROR, "S = 0\n");
1070 | return false;
1071 | }
1072 |
1073 | if (param[1] > param[3]) {
1074 | log(ERROR, "H > MAX_HEAD\n");
1075 | return false;
1076 | }
1077 |
1078 | if (param[2] > param[4]) {
1079 | log(ERROR, "S > MAX_SECTOR\n");
1080 | return false;
1081 | }
1082 |
1083 | *lba = (maxuint_t)param[3] * param[4] * param[0]; /* MH * MS * C */
1084 | *lba += (maxuint_t)param[4] * param[1]; /* MS * H */
1085 |
1086 | *lba += param[2] - 1; /* S - 1 */
1087 |
1088 | printf("\033[1mCHS2LBA\033[0m\n");
1089 | printf(" C:%lu H:%lu S:%lu MAX_HEAD:%lu MAX_SECTOR:%lu\n",
1090 | param[0], param[1], param[2], param[3], param[4]);
1091 |
1092 | return true;
1093 | }
1094 |
1095 | static bool lba2chs(char *lba, t_chs *p_chs)
1096 | {
1097 | int token_no = 0;
1098 | char *ptr, *token;
1099 | ull param[3] = {0, MAX_HEAD, MAX_SECTOR};
1100 |
1101 | ptr = token = lba;
1102 |
1103 | while (*ptr && token_no < 3) {
1104 | if (*ptr == '-') {
1105 | *ptr = '\0';
1106 | param[token_no] = strtoull_b(token);
1107 | ++token_no;
1108 | *ptr = '-';
1109 | ++ptr;
1110 | token = ptr;
1111 |
1112 | if (*ptr == '\0' && token_no < 3) {
1113 | param[token_no] = strtoull_b(token);
1114 | ++token_no;
1115 | }
1116 |
1117 | continue;
1118 | }
1119 |
1120 | ++ptr;
1121 |
1122 | if (*ptr == '\0' && token_no < 3) {
1123 | param[token_no] = strtoull_b(token);
1124 | ++token_no;
1125 | }
1126 | }
1127 |
1128 | /* Fail if LBA is omitted */
1129 | if (!token_no) {
1130 | log(ERROR, "LBA missing\n");
1131 | return false;
1132 | }
1133 |
1134 | if (!param[1]) {
1135 | log(ERROR, "MAX_HEAD = 0\n");
1136 | return false;
1137 | }
1138 |
1139 | if (!param[2]) {
1140 | log(ERROR, "MAX_SECTOR = 0\n");
1141 | return false;
1142 | }
1143 |
1144 | /* L / (MS * MH) */
1145 | p_chs->c = (ulong)(param[0] / (param[2] * param[1]));
1146 | /* (L / MS) % MH */
1147 | p_chs->h = (ulong)((param[0] / param[2]) % param[1]);
1148 | if (p_chs->h > MAX_HEAD) {
1149 | log(ERROR, "H > MAX_HEAD\n");
1150 | return false;
1151 | }
1152 |
1153 | /* (L % MS) + 1 */
1154 | p_chs->s = (ulong)((param[0] % param[2]) + 1);
1155 | if (p_chs->s > MAX_SECTOR) {
1156 | log(ERROR, "S > MAX_SECTOR\n");
1157 | return false;
1158 | }
1159 |
1160 | printf("\033[1mLBA2CHS\033[0m\n LBA:%s ",
1161 | getstr_u128(param[0], uint_buf));
1162 | printf("MAX_HEAD:%s ", getstr_u128(param[1], uint_buf));
1163 | printf("MAX_SECTOR:%s\n", getstr_u128(param[2], uint_buf));
1164 |
1165 | return true;
1166 | }
1167 |
1168 | static void show_basic_sizes()
1169 | {
1170 | printf("---------------\n Storage sizes\n---------------\n"
1171 | "char : %lu\n"
1172 | "short : %lu\n"
1173 | "int : %lu\n"
1174 | "long : %lu\n"
1175 | "long long : %lu\n"
1176 | #ifdef __SIZEOF_INT128__
1177 | "__int128_t : %lu\n"
1178 | #else
1179 | "__int64_t : %lu\n"
1180 | #endif
1181 | "float : %lu\n"
1182 | "double : %lu\n"
1183 | "long double: %lu\n",
1184 | sizeof(unsigned char),
1185 | sizeof(unsigned short),
1186 | sizeof(unsigned int),
1187 | sizeof(unsigned long),
1188 | sizeof(unsigned long long),
1189 | sizeof(maxuint_t),
1190 | sizeof(float),
1191 | sizeof(double),
1192 | sizeof(long double));
1193 | }
1194 |
1195 | static void prompt_help()
1196 | {
1197 | printf("prompt keys:\n\
1198 | b toggle bc mode\n\
1199 | r show result from last operation\n\
1200 | s show sizes of storage types\n\
1201 | ? show prompt help\n\
1202 | q/double ↵ quit program\n");
1203 | }
1204 |
1205 | static void usage()
1206 | {
1207 | printf("usage: bcal [-c N] [-f loc] [-s bytes] [expr]\n\
1208 | [N [unit]] [-b [expr]] [-m] [-d] [-h]\n\n\
1209 | Storage expression calculator.\n\n\
1210 | positional arguments:\n\
1211 | expr expression in decimal/hex operands\n\
1212 | N [unit] capacity in B/KiB/MiB/GiB/TiB/kB/MB/GB/TB\n\
1213 | https://en.wikipedia.org/wiki/Binary_prefix\n\
1214 | default unit is B (byte), case is ignored\n\
1215 | N can be decimal or '0x' prefixed hex value\n\n\
1216 | optional arguments:\n\
1217 | -c N show +ve integer N in binary, decimal, hex\n\
1218 | -f loc convert CHS to LBA or LBA to CHS\n\
1219 | refer to the operational notes in man page\n\
1220 | -s bytes sector size [default 512]\n\
1221 | -b [expr] enter bc mode or evaluate expression in bc\n\
1222 | -m show minimal output (e.g. decimal bytes)\n\
1223 | -d enable debug information and logs\n\
1224 | -h show this help\n\n");
1225 |
1226 | prompt_help();
1227 |
1228 | printf("\nVersion %s\n\
1229 | Copyright © 2016 Arun Prakash Jana \n\
1230 | License: GPLv3\n\
1231 | Webpage: https://github.com/jarun/bcal\n", VERSION);
1232 | }
1233 |
1234 | static int bstricmp(const char *s1, const char *s2)
1235 | {
1236 | while ((int)*s1 && (tolower((int)*s1) == tolower((int)*s2))) {
1237 | ++s1;
1238 | ++s2;
1239 | }
1240 | return *(const unsigned char *)s1 - *(const unsigned char *)s2;
1241 | }
1242 |
1243 | /* Convert any unit in bytes
1244 | * Failure if out parameter holds -1
1245 | */
1246 | static maxuint_t unitconv(Data bunit, char *isunit, int *out)
1247 | {
1248 | /* Data is a C structure containing a string p and a char
1249 | * indicating if the string is a unit or a plain number
1250 | */
1251 | char *numstr = bunit.p, *punit = NULL;
1252 | int count;
1253 | maxfloat_t byte_metric = 0;
1254 |
1255 | if (numstr == NULL || *numstr == '\0') {
1256 | log(ERROR, "invalid token\n");
1257 | *out = -1;
1258 | return 0;
1259 | }
1260 |
1261 | log(DEBUG, "numstr: %s\n", numstr);
1262 | *out = 0;
1263 |
1264 | /* ensure this is not the result of a previous operation */
1265 | if (*isunit != 1)
1266 | *isunit = 0;
1267 |
1268 | byte_metric = strtold(numstr, &punit);
1269 | log(DEBUG, "byte_metric: %Lf\n", byte_metric);
1270 | if (*numstr != '\0' && *punit == '\0')
1271 | return (maxuint_t)byte_metric;
1272 |
1273 | log(DEBUG, "punit: %s\n", punit);
1274 |
1275 | count = ARRAY_SIZE(units);
1276 | while (--count >= 0)
1277 | if (!bstricmp(units[count], punit))
1278 | break;
1279 |
1280 | if (count == -1) {
1281 | if (cfg.minimal)
1282 | log(ERROR, "unknown unit\n");
1283 | else
1284 | try_bc(NULL);
1285 |
1286 | *out = -1;
1287 | return 0;
1288 | }
1289 |
1290 | *isunit = 1;
1291 |
1292 | switch (count) {
1293 | case 0:
1294 | return (maxuint_t)byte_metric;
1295 | case 1: /* Kibibyte */
1296 | return (maxuint_t)(byte_metric * 1024);
1297 | case 2: /* Mebibyte */
1298 | return (maxuint_t)(byte_metric * (1 << 20));
1299 | case 3: /* Gibibyte */
1300 | return (maxuint_t)(byte_metric * (1 << 30));
1301 | case 4: /* Tebibyte */
1302 | return (maxuint_t)(byte_metric * ((maxuint_t)1 << 40));
1303 | case 5: /* Kilobyte */
1304 | return (maxuint_t)(byte_metric * 1000);
1305 | case 6: /* Megabyte */
1306 | return (maxuint_t)(byte_metric * 1000000);
1307 | case 7: /* Gigabyte */
1308 | return (maxuint_t)(byte_metric * 1000000000);
1309 | case 8: /* Terabyte */
1310 | return (maxuint_t)(byte_metric * 1000000000000);
1311 | default:
1312 | log(ERROR, "unknown unit\n");
1313 | *out = -1;
1314 | return 0;
1315 | }
1316 | }
1317 |
1318 | /* Get the priority of operators.
1319 | * Higher priority, higher value.
1320 | */
1321 | static int priority(char sign) /* Get the priority of operators, higher priprity */
1322 | {
1323 | switch (sign) {
1324 | case '|': return 1;
1325 | case '^': return 2;
1326 | case '&': return 3;
1327 | case '>':
1328 | case '<': return 4;
1329 | case '-':
1330 | case '+': return 5;
1331 | case '%':
1332 | case '/':
1333 | case '*': return 6;
1334 | default : return 0;
1335 | }
1336 |
1337 | return 0;
1338 | }
1339 |
1340 | /* Convert Infix mathematical expression to Postfix */
1341 | static int infix2postfix(char *exp, queue **resf, queue **resr)
1342 | {
1343 | stack *op = NULL; /* Operator Stack */
1344 | char *token = strtok(exp, " ");
1345 | static Data tokenData, ct;
1346 | int balanced = 0;
1347 | bool tokenize = true;
1348 |
1349 | tokenData.p[0] = '\0';
1350 | tokenData.unit = 0;
1351 |
1352 | log(DEBUG, "exp: %s\n", exp);
1353 | log(DEBUG, "token: %s\n", token);
1354 |
1355 | while (token) {
1356 | /* Copy argument to string part of the structure */
1357 | bstrlcpy(tokenData.p, token, NUM_LEN);
1358 |
1359 | switch (token[0]) {
1360 | case '+':
1361 | case '-':
1362 | case '*':
1363 | case '/':
1364 | case '%':
1365 | case '>':
1366 | case '<':
1367 | case '&':
1368 | case '|':
1369 | case '^':
1370 | if (token[1] != '\0') {
1371 | log(ERROR, "invalid token terminator\n");
1372 | emptystack(&op);
1373 | cleanqueue(resf);
1374 | return -1;
1375 | }
1376 |
1377 | while (!isempty(op) && top(op)[0] != '(' &&
1378 | priority(token[0]) <= priority(top(op)[0])) {
1379 | /* Pop from operator stack */
1380 | pop(&op, &ct);
1381 | /* Insert to Queue */
1382 | enqueue(resf, resr, ct);
1383 | }
1384 |
1385 | push(&op, tokenData);
1386 | break;
1387 | case '(':
1388 | ++balanced;
1389 | push(&op, tokenData);
1390 | break;
1391 | case ')':
1392 | while (!isempty(op) && top(op)[0] != '(') {
1393 | pop(&op, &ct);
1394 | enqueue(resf, resr, ct);
1395 | }
1396 |
1397 | pop(&op, &ct);
1398 | --balanced;
1399 | break;
1400 | case 'r':
1401 | if (lastres.p[0] == '\0') {
1402 | log(ERROR, "no result stored\n");
1403 | emptystack(&op);
1404 | cleanqueue(resf);
1405 | return -1;
1406 | }
1407 |
1408 | enqueue(resf, resr, lastres);
1409 | break;
1410 | default:
1411 | /*
1412 | * Check if unit is specified
1413 | * This also guards against a case of 0xn b
1414 | */
1415 | token = strtok(NULL, " ");
1416 | if (token && token[0] == 'b' && token[1] == '\0') {
1417 | tokenData.unit = 1;
1418 | log(DEBUG, "unit found\n");
1419 | } else
1420 | tokenize = false; /* We already toknized here */
1421 |
1422 | /* Enqueue operands */
1423 | log(DEBUG, "tokenData: %s %d\n", tokenData.p, tokenData.unit);
1424 | enqueue(resf, resr, tokenData);
1425 | if (tokenize)
1426 | tokenData.unit = 0;
1427 | }
1428 |
1429 | if (tokenize)
1430 | token = strtok(NULL, " ");
1431 | else
1432 | tokenize = true;
1433 |
1434 | log(DEBUG, "token: %s\n", token);
1435 | }
1436 |
1437 | while (!isempty(op)) {
1438 | /* Put remaining elements into the queue */
1439 | pop(&op, &ct);
1440 | enqueue(resf, resr, ct);
1441 | }
1442 |
1443 | if (balanced != 0) {
1444 | log(ERROR, "unbalanced expression\n");
1445 | cleanqueue(resf);
1446 | return -1;
1447 | }
1448 |
1449 | return 0;
1450 | }
1451 |
1452 | /*
1453 | * Checks for underflow in division
1454 | * Returns:
1455 | * 0 - no issues
1456 | * -1 - underflow
1457 | */
1458 | static int validate_div(maxuint_t dividend, maxuint_t divisor, maxuint_t quotient)
1459 | {
1460 | if (divisor * quotient < dividend) {
1461 | log(WARNING, "result truncated\n");
1462 |
1463 | if (cfg.loglvl == DEBUG) {
1464 | printhex_u128(dividend);
1465 | printf(" (dividend)\n");
1466 | printhex_u128(divisor);
1467 | printf(" (divisor)\n");
1468 | printhex_u128(quotient);
1469 | printf(" (quotient)\n");
1470 | }
1471 |
1472 | return -1;
1473 | }
1474 |
1475 | return 0;
1476 | }
1477 |
1478 | /* Evaluates Postfix Expression
1479 | * Numeric result if out parameter holds 1
1480 | * Failure if out parameter holds -1
1481 | */
1482 | static maxuint_t eval(queue **front, queue **rear, int *out)
1483 | {
1484 | stack *est = NULL;
1485 | Data res, arg, raw_a, raw_b, raw_c;
1486 | *out = 0;
1487 | maxuint_t a, b, c;
1488 |
1489 | /* Check if queue is empty */
1490 | if (*front == NULL)
1491 | return 0;
1492 |
1493 | /* Check if only one element in the queue */
1494 | if (*front == *rear) {
1495 | char unit = 0;
1496 |
1497 | dequeue(front, rear, &res);
1498 | return unitconv(res, &unit, out);
1499 | }
1500 |
1501 | while (*front) {
1502 | dequeue(front, rear, &arg);
1503 |
1504 | /* Check if arg is an operator */
1505 | if (strlen(arg.p) == 1 && !isdigit((int)arg.p[0])) {
1506 | pop(&est, &raw_b);
1507 | pop(&est, &raw_a);
1508 |
1509 | b = unitconv(raw_b, &raw_b.unit, out);
1510 | if (*out == -1)
1511 | return 0;
1512 | a = unitconv(raw_a, &raw_a.unit, out);
1513 | if (*out == -1)
1514 | return 0;
1515 |
1516 | log(DEBUG, "(%s, %d) %c (%s, %d)\n",
1517 | raw_a.p, raw_a.unit, arg.p[0], raw_b.p, raw_b.unit);
1518 |
1519 | c = 0;
1520 | raw_c.unit = 0;
1521 |
1522 | switch (arg.p[0]) {
1523 | case '>':
1524 | case '<':
1525 | if (raw_b.unit) {
1526 | log(ERROR, "unit mismatch in %c%c\n", arg.p[0], arg.p[0]);
1527 | goto error;
1528 | }
1529 |
1530 | if (arg.p[0] == '>')
1531 | c = a >> b;
1532 | else
1533 | c = a << b;
1534 | raw_c.unit = raw_a.unit;
1535 | break;
1536 | case '+':
1537 | case '&':
1538 | case '|':
1539 | case '^':
1540 | /* Check if the units match */
1541 | if (raw_a.unit == raw_b.unit) {
1542 | switch (arg.p[0]) {
1543 | case '+':
1544 | c = a + b;
1545 | break;
1546 | case '&':
1547 | c = a & b;
1548 | break;
1549 | case '|':
1550 | c = a | b;
1551 | break;
1552 | case '^':
1553 | c = a ^ b;
1554 | break;
1555 | default:
1556 | break;
1557 | }
1558 |
1559 | if (raw_a.unit)
1560 | raw_c.unit = 1;
1561 | break;
1562 | }
1563 |
1564 | log(ERROR, "unit mismatch in %c\n", arg.p[0]);
1565 | goto error;
1566 | case '-':
1567 | /* Check if the units match */
1568 | if (raw_a.unit == raw_b.unit) {
1569 | if (b > a) {
1570 | log(ERROR, "negative result\n");
1571 | goto error;
1572 | }
1573 |
1574 | c = a - b;
1575 | if (raw_a.unit)
1576 | raw_c.unit = 1;
1577 | break;
1578 | }
1579 |
1580 | log(ERROR, "unit mismatch in -\n");
1581 | goto error;
1582 | case '*':
1583 | /* Check if only one is unit */
1584 | if (!(raw_a.unit && raw_b.unit)) {
1585 | c = a * b;
1586 | if (raw_a.unit || raw_b.unit)
1587 | raw_c.unit = 1;
1588 | break;
1589 | }
1590 |
1591 | log(ERROR, "unit mismatch in *\n");
1592 | goto error;
1593 | case '/':
1594 | if (b == 0) {
1595 | log(ERROR, "division by 0\n");
1596 | goto error;
1597 | }
1598 |
1599 | if (raw_a.unit && raw_b.unit) {
1600 | c = a / b;
1601 |
1602 | validate_div(a, b, c);
1603 | break;
1604 | }
1605 |
1606 | if (!raw_b.unit) {
1607 | c = a / b;
1608 | if (raw_a.unit)
1609 | raw_c.unit = 1;
1610 |
1611 | validate_div(a, b, c);
1612 | break;
1613 | }
1614 |
1615 | log(ERROR, "unit mismatch in /\n");
1616 | goto error;
1617 | case '%':
1618 | if (b == 0) {
1619 | log(ERROR, "division by 0\n");
1620 | goto error;
1621 | }
1622 |
1623 | if (!(raw_a.unit || raw_b.unit)) {
1624 | c = a % b;
1625 | break;
1626 | }
1627 |
1628 | log(ERROR, "unit mismatch in modulo\n"); // fallthrough
1629 | default:
1630 | goto error;
1631 | }
1632 |
1633 | /* Convert to string */
1634 | bstrlcpy(raw_c.p, getstr_u128(c, uint_buf), NUM_LEN);
1635 | log(DEBUG, "c: %s unit: %d\n", raw_c.p, raw_c.unit);
1636 |
1637 | /* Push to stack */
1638 | push(&est, raw_c);
1639 |
1640 | } else {
1641 | log(DEBUG, "pushing (%s %d)\n", arg.p, arg.unit);
1642 | push(&est, arg);
1643 | }
1644 | }
1645 |
1646 | pop(&est, &res);
1647 |
1648 | /* Stack must be empty at this point */
1649 | if (!isempty(est)) {
1650 | log(ERROR, "invalid expression\n");
1651 | goto error;
1652 | }
1653 |
1654 | if (res.unit == 0)
1655 | *out = 1;
1656 |
1657 | /* Convert string to integer */
1658 | return strtoull(res.p, NULL, 0);
1659 |
1660 | error:
1661 | *out = -1;
1662 | emptystack(&est);
1663 | cleanqueue(front);
1664 | return 0;
1665 | }
1666 |
1667 | static int issign(char c)
1668 | {
1669 | switch (c) {
1670 | case '+':
1671 | case '-':
1672 | case '*':
1673 | case '/':
1674 | case '%':
1675 | case '>':
1676 | case '<':
1677 | case '&':
1678 | case '|':
1679 | case '^':
1680 | return 1;
1681 | default:
1682 | return 0;
1683 | }
1684 | }
1685 |
1686 | /* Check if a char is operator or not */
1687 | static int isoperator(int c)
1688 | {
1689 | switch (c) {
1690 | case '+':
1691 | case '-':
1692 | case '*':
1693 | case '/':
1694 | case '%':
1695 | case '>':
1696 | case '<':
1697 | case '&':
1698 | case '|':
1699 | case '^':
1700 | case '(':
1701 | case ')': return 1;
1702 | default: return 0;
1703 | }
1704 | }
1705 |
1706 | /* Check if valid storage arithmetic expression */
1707 | /*
1708 | static int checkexp(char *exp)
1709 | {
1710 | while (*exp) {
1711 | if (*exp == 'b' || *exp == 'B')
1712 | return 1;
1713 |
1714 | ++exp;
1715 | }
1716 |
1717 | return 0;
1718 | }
1719 | */
1720 |
1721 | /* Trim ending newline and whitespace from both ends, in place */
1722 | static void strstrip(char *s)
1723 | {
1724 | if (!s || !*s)
1725 | return;
1726 |
1727 | int len = (int)strlen(s) - 1;
1728 |
1729 | if (s[len] == '\n')
1730 | --len;
1731 | while (len >= 0 && (isspace((int)s[len]) || s[len] == '\"' || s[len] == '\''))
1732 | --len;
1733 | s[len + 1] = '\0';
1734 |
1735 | len = 0;
1736 | while (s[len] && (isspace((int)s[len]) || s[len] == '\"' || s[len] == '\''))
1737 | ++len;
1738 |
1739 | if (len) {
1740 | while (s[len]) {
1741 | *s = s[len];
1742 | ++s;
1743 | }
1744 |
1745 | *s = '\0';
1746 | }
1747 | }
1748 |
1749 | /* Replace consecutive inner whitespaces with a single space */
1750 | static void removeinnerspaces(char *s)
1751 | {
1752 | char *p = s;
1753 |
1754 | while (*s != '\0') {
1755 | /* We should not combine 0xn b*/
1756 | if (!isspace((int)*s) || (*(s + 1) == 'b')) {
1757 | *p = *s;
1758 | ++p;
1759 | }
1760 |
1761 | ++s;
1762 | }
1763 |
1764 | *p = '\0';
1765 | }
1766 |
1767 | /* Make the expression compatible with parsing by
1768 | * inserting/removing space between arguments
1769 | */
1770 | static char *fixexpr(char *exp, int *unitless)
1771 | {
1772 | *unitless = 0;
1773 |
1774 | strstrip(exp);
1775 | removeinnerspaces(exp);
1776 |
1777 | /*
1778 | if (!checkexp(exp)) {
1779 | log(DEBUG, "no unit in expression [%s]\n", exp);
1780 | *unitless = 1;
1781 | return NULL;
1782 | }
1783 | */
1784 |
1785 | int i = 0, j = 0;
1786 | char *parsed = (char *)calloc(1, 2 * strlen(exp) * sizeof(char));
1787 | char prev = '(';
1788 |
1789 | log(DEBUG, "exp (%s)\n", exp);
1790 |
1791 | while (exp[i] != '\0') {
1792 | if (exp[i] == '{' || exp[i] == '}' || exp[i] == '[' || exp[i] == ']') {
1793 | log(ERROR, "first brackets only\n");
1794 | free(parsed);
1795 | return NULL;
1796 | }
1797 |
1798 | if (exp[i] == '-' && (issign(prev) || prev == '(')) {
1799 | log(ERROR, "negative token\n");
1800 | free(parsed);
1801 | return NULL;
1802 | }
1803 |
1804 | if (isoperator((int)exp[i]) && isalpha((int)exp[i + 1]) && (exp[i + 1] != 'r')) {
1805 | log(ERROR, "invalid expression\n");
1806 | free(parsed);
1807 | return NULL;
1808 | }
1809 |
1810 | if ((isdigit((int)exp[i]) && isoperator((int)exp[i + 1])) ||
1811 | (isoperator((int)exp[i]) && (isdigit((int)exp[i + 1]) ||
1812 | isoperator((int)exp[i + 1]))) ||
1813 | (isalpha((int)exp[i]) && isoperator((int)exp[i + 1])) ||
1814 | (isoperator((int)exp[i]) && ((int)exp[i + 1] == 'r'))) {
1815 | if (exp[i] == '<' || exp[i] == '>') { /* handle shift operators << and >> */
1816 | if (prev != exp[i] && exp[i] != exp[i + 1]) {
1817 | log(ERROR, "invalid operator %c\n", exp[i]);
1818 | *unitless = 0;
1819 | free(parsed);
1820 | return NULL;
1821 | }
1822 |
1823 | if (prev == exp[i + 1]) { /* handle <<< or >>> */
1824 | log(ERROR, "invalid sequence %c%c%c\n", prev, exp[i], exp[i + 1]);
1825 | *unitless = 0;
1826 | free(parsed);
1827 | return NULL;
1828 | }
1829 |
1830 | if (exp[i] == exp[i + 1])
1831 | goto loop_end;
1832 | }
1833 |
1834 | parsed[j] = exp[i];
1835 | ++j;
1836 | parsed[j] = ' ';
1837 | ++j;
1838 | parsed[j] = exp[i + 1];
1839 | } else {
1840 | parsed[j] = exp[i];
1841 | ++j;
1842 | }
1843 |
1844 | loop_end:
1845 | prev = exp[i];
1846 | ++i;
1847 | }
1848 |
1849 | if (parsed[j])
1850 | parsed[++j] = '\0';
1851 |
1852 | log(DEBUG, "parsed (%s)\n", parsed);
1853 |
1854 | /* If there's no space, this is either
1855 | * a number or malformed expression
1856 | */
1857 | i = 0;
1858 | while (parsed[i] && parsed[i] != ' ')
1859 | ++i;
1860 |
1861 | if (!parsed[i]) {
1862 | log(DEBUG, "no operator in expression [%s]\n", parsed);
1863 | free(parsed);
1864 | *unitless = 1;
1865 | return NULL;
1866 | }
1867 |
1868 | return parsed;
1869 | }
1870 |
1871 | static int convertunit(char *value, char *unit, ulong sectorsz)
1872 | {
1873 | int count = ARRAY_SIZE(units), ret;
1874 | maxuint_t bytes = 0, lba = 0, offset = 0;
1875 |
1876 | strstrip(value);
1877 | if (value[0] == '\0') {
1878 | log(ERROR, "invalid value\n");
1879 | return -1;
1880 | }
1881 |
1882 | if (!unit) {
1883 | int unitchars = 0, len = (int)strlen(value);
1884 |
1885 | while (len) {
1886 | if (!isalpha((int)value[len - 1]))
1887 | break;
1888 |
1889 | ++unitchars;
1890 | --len;
1891 | }
1892 |
1893 | if (unitchars) {
1894 | while (--count >= 0)
1895 | if (!bstricmp(units[count], value + len))
1896 | break;
1897 |
1898 | if (count == -1) {
1899 | log(ERROR, "unknown unit\n");
1900 | return -1;
1901 | }
1902 |
1903 | value[len] = '\0';
1904 | } else
1905 | count = 0;
1906 | } else {
1907 | strstrip(unit);
1908 |
1909 | while (--count >= 0)
1910 | if (!bstricmp(units[count], unit))
1911 | break;
1912 |
1913 | if (count == -1) {
1914 | log(ERROR, "unknown unit\n");
1915 | return -1;
1916 | }
1917 | }
1918 |
1919 | log(DEBUG, "%s %s\n", value, units[count]);
1920 |
1921 | if (!cfg.minimal && unit)
1922 | printf("\033[1mUNIT CONVERSION\033[0m\n");
1923 |
1924 | switch (count) {
1925 | case 0:
1926 | bytes = convertbyte(value, &ret);
1927 | break;
1928 | case 1:
1929 | bytes = convertkib(value, &ret);
1930 | break;
1931 | case 2:
1932 | bytes = convertmib(value, &ret);
1933 | break;
1934 | case 3:
1935 | bytes = convertgib(value, &ret);
1936 | break;
1937 | case 4:
1938 | bytes = converttib(value, &ret);
1939 | break;
1940 | case 5:
1941 | bytes = convertkb(value, &ret);
1942 | break;
1943 | case 6:
1944 | bytes = convertmb(value, &ret);
1945 | break;
1946 | case 7:
1947 | bytes = convertgb(value, &ret);
1948 | break;
1949 | case 8:
1950 | bytes = converttb(value, &ret);
1951 | break;
1952 | default:
1953 | log(ERROR, "unknown unit\n");
1954 | return -1;
1955 | }
1956 |
1957 | if (ret == -1) {
1958 | if (cfg.minimal || unit) /* For running python test cases */
1959 | log(ERROR, "malformed input\n");
1960 | else
1961 | return try_bc(NULL);
1962 |
1963 | return -1;
1964 | }
1965 |
1966 | bstrlcpy(lastres.p, getstr_u128(bytes, uint_buf), UINT_BUF_LEN);
1967 | lastres.unit = 1;
1968 | log(DEBUG, "result: %s %d\n", lastres.p, lastres.unit);
1969 |
1970 | if (cfg.minimal)
1971 | return 0;
1972 |
1973 | printf("\nADDRESS\n (d) %s\n (h) ",
1974 | getstr_u128(bytes, uint_buf));
1975 | printhex_u128(bytes);
1976 |
1977 | /* Calculate LBA and offset */
1978 | lba = bytes / sectorsz;
1979 | offset = bytes % sectorsz;
1980 |
1981 | printf("\n\nLBA:OFFSET (sector size: 0x%lx)\n", sectorsz);
1982 | /* We use a global buffer, so print decimal lba first, then offset */
1983 | printf(" (d) %s:", getstr_u128(lba, uint_buf));
1984 | printf("%s\n (h) ", getstr_u128(offset, uint_buf));
1985 | printhex_u128(lba);
1986 | printf(":");
1987 | printhex_u128(offset);
1988 | printf("\n");
1989 |
1990 | return 0;
1991 | }
1992 |
1993 | static int evaluate(char *exp, ulong sectorsz)
1994 | {
1995 | int ret = 0;
1996 | maxuint_t bytes = 0;
1997 | queue *front = NULL, *rear = NULL;
1998 | char *expr = fixexpr(exp, &ret); /* Make parsing compatible */
1999 | char *ptr;
2000 |
2001 | if (expr)
2002 | log(DEBUG, "expr: %s\n", expr);
2003 |
2004 | if (expr == NULL) {
2005 | if (ret)
2006 | return convertunit(exp, NULL, sectorsz);
2007 |
2008 | return -1;
2009 | }
2010 |
2011 | ret = infix2postfix(expr, &front, &rear);
2012 | free(expr);
2013 | if (ret == -1)
2014 | return -1;
2015 |
2016 | bytes = eval(&front, &rear, &ret); /* Evaluate Expression */
2017 | if (ret == -1)
2018 | return -1;
2019 |
2020 | if (ret == 1) {
2021 | ptr = getstr_u128(bytes, uint_buf);
2022 | printf("%s\n", ptr);
2023 | bstrlcpy(lastres.p, getstr_u128(bytes, uint_buf), UINT_BUF_LEN);
2024 | lastres.unit = 0;
2025 | log(DEBUG, "result1: %s %d\n", lastres.p, lastres.unit);
2026 | return 0;
2027 | }
2028 |
2029 | if (!(cfg.minimal || cfg.repl))
2030 | printf("\033[1mRESULT\033[0m\n");
2031 |
2032 | convertbyte(getstr_u128(bytes, uint_buf), &ret);
2033 | if (ret == -1) {
2034 | log(ERROR, "malformed input\n");
2035 | return -1;
2036 | }
2037 |
2038 | ptr = getstr_u128(bytes, uint_buf);
2039 | bstrlcpy(lastres.p, ptr, UINT_BUF_LEN);
2040 | lastres.unit = 1;
2041 | log(DEBUG, "result2: %s %d\n", lastres.p, lastres.unit);
2042 |
2043 | if (cfg.minimal)
2044 | return 0;
2045 |
2046 | printf("\nADDRESS\n (d) %s\n (h) ", ptr);
2047 | printhex_u128(bytes);
2048 | printf("\n");
2049 |
2050 | return 0;
2051 | }
2052 |
2053 | int convertbase(char *arg)
2054 | {
2055 | char *pch;
2056 |
2057 | strstrip(arg);
2058 |
2059 | if (*arg == '\0') {
2060 | log(ERROR, "empty input\n");
2061 | return -1;
2062 | }
2063 |
2064 | if (*arg == '-') {
2065 | log(ERROR, "N must be >= 0\n");
2066 | return -1;
2067 | }
2068 |
2069 | if (cfg.repl && arg[0] == 'r' && arg[1] == '\0')
2070 | arg = lastres.p;
2071 |
2072 | maxuint_t val = strtouquad(arg, &pch);
2073 | if (*pch) {
2074 | log(ERROR, "invalid input\n");
2075 | return -1;
2076 | }
2077 |
2078 | printf(" (b) ");
2079 | binprint(val);
2080 | printf("\n (d) %s\n (h) ",
2081 | getstr_u128(val, uint_buf));
2082 | printhex_u128(val);
2083 | printf("\n");
2084 |
2085 | return 0;
2086 | }
2087 |
2088 | int main(int argc, char **argv)
2089 | {
2090 | int opt = 0, operation = 0;
2091 | ulong sectorsz = SECTOR_SIZE;
2092 |
2093 | if (getenv("BCAL_USE_CALC"))
2094 | cfg.calc = true;
2095 |
2096 | opterr = 0;
2097 | rl_bind_key('\t', rl_insert);
2098 |
2099 | while ((opt = getopt(argc, argv, "bc:df:hms:")) != -1) {
2100 | switch (opt) {
2101 | case 'c':
2102 | {
2103 | operation = 1;
2104 | convertbase(optarg);
2105 | printf("\n");
2106 | break;
2107 | }
2108 | case 'f':
2109 | operation = 1;
2110 |
2111 | if (tolower((int)*optarg) == 'c') {
2112 | maxuint_t lba = 0;
2113 |
2114 | if (chs2lba(optarg + 1, &lba)) {
2115 | printf(" LBA: (d) %s, (h) ",
2116 | getstr_u128(lba, uint_buf));
2117 | printhex_u128(lba);
2118 | printf("\n\n");
2119 | }
2120 | } else if (tolower((int)*optarg) == 'l') {
2121 | t_chs chs;
2122 |
2123 | if (lba2chs(optarg + 1, &chs)) {
2124 | printf(" CHS: (d) %lu %lu %lu, ",
2125 | chs.c, chs.h, chs.s);
2126 | printf("(h) 0x%lx 0x%lx 0x%lx\n\n",
2127 | chs.c, chs.h, chs.s);
2128 | }
2129 | } else
2130 | log(ERROR, "invalid input\n");
2131 | break;
2132 | case 'm':
2133 | cfg.minimal = 1;
2134 | break;
2135 | case 's':
2136 | if (*optarg == '-') {
2137 | log(ERROR, "sector size must be +ve\n");
2138 | return -1;
2139 | }
2140 | sectorsz = strtoul_b(optarg);
2141 | break;
2142 | case 'b':
2143 | cfg.bcmode = 1;
2144 | if (cfg.calc)
2145 | strncpy(prompt, "calc> ", 7);
2146 | else
2147 | strncpy(prompt, "bc> ", 5);
2148 | break;
2149 | case 'd':
2150 | cfg.loglvl = DEBUG;
2151 | log(DEBUG, "bcal v%s\n", VERSION);
2152 | log(DEBUG, "maxuint_t is %lu bytes\n", sizeof(maxuint_t));
2153 |
2154 | break;
2155 | case 'h':
2156 | usage();
2157 | return 0;
2158 | default:
2159 | log(ERROR, "invalid option \'%c\'\n\n", (char)optopt);
2160 | usage();
2161 | return -1;
2162 | }
2163 | }
2164 |
2165 | log(DEBUG, "argc %d, optind %d\n", argc, optind);
2166 |
2167 | if (!operation && (argc == optind)) {
2168 | char *ptr = NULL, *tmp = NULL;
2169 | cfg.repl = 1;
2170 | int enters = 0;
2171 |
2172 | read_history(NULL);
2173 |
2174 | printf("q/double Enter -> quit, ? -> help\n");
2175 | while ((tmp = readline(prompt)) != NULL) {
2176 | if (!tmp)
2177 | exit(0);
2178 |
2179 | if (program_exit(tmp)) {
2180 | free(tmp);
2181 | exit(0);
2182 | }
2183 |
2184 | /* Quit on double Enter */
2185 | if (tmp[0] == '\0') {
2186 | if (enters == 1) {
2187 | free(tmp);
2188 | break;
2189 | }
2190 |
2191 | ++enters;
2192 | free(tmp);
2193 | continue;
2194 | }
2195 |
2196 | enters = 0;
2197 |
2198 | /* Save the original pointer from readline() */
2199 | ptr = tmp;
2200 |
2201 | strstrip(tmp);
2202 | remove_commas(tmp);
2203 |
2204 | if (tmp[0] == '\0') {
2205 | free(ptr);
2206 | continue;
2207 | }
2208 |
2209 | log(DEBUG, "ptr: [%s]\n", ptr);
2210 | log(DEBUG, "tmp: [%s]\n", ptr);
2211 |
2212 | add_history(tmp);
2213 |
2214 | if ((strlen(tmp) == 1) && tmp[1] == '\0') {
2215 | switch (tmp[0]) {
2216 | case 'r':
2217 | /* Show the last stored result */
2218 | if (lastres.p[0] == '\0')
2219 | printf("no result stored\n");
2220 | else {
2221 | printf("r = %s ", lastres.p);
2222 | if (lastres.unit)
2223 | printf("B");
2224 | printf("\n");
2225 | }
2226 |
2227 | free(ptr);
2228 | continue;
2229 | case 'b':
2230 | cfg.bcmode ^= 1;
2231 | if (cfg.bcmode) {
2232 | if (cfg.calc)
2233 | strncpy(prompt, "calc> ", 7);
2234 | else {
2235 | printf("bc vars: scale = 10, ibase = 10\n");
2236 | strncpy(prompt, "bc> ", 5);
2237 | }
2238 | } else
2239 | strncpy(prompt, "bcal> ", 7);
2240 |
2241 | free(ptr);
2242 | continue;
2243 | case '?':
2244 | prompt_help();
2245 |
2246 | free(ptr);
2247 | continue;
2248 | case 'q':
2249 | free(ptr);
2250 | write_history(NULL);
2251 | return 0;
2252 | case 's':
2253 | show_basic_sizes();
2254 |
2255 | free(ptr);
2256 | continue;
2257 | default:
2258 | printf("invalid input\n");
2259 | free(ptr);
2260 | continue;
2261 | }
2262 | }
2263 |
2264 | if (tmp[0] == 'c') {
2265 | convertbase(tmp + 1);
2266 | free(ptr);
2267 | continue;
2268 | }
2269 |
2270 | if (cfg.bcmode) {
2271 | try_bc(tmp);
2272 | free(ptr);
2273 | continue;
2274 | }
2275 |
2276 | curexpr = tmp;
2277 |
2278 | /* Evaluate the expression */
2279 | evaluate(tmp, sectorsz);
2280 |
2281 | free(ptr);
2282 | }
2283 |
2284 | write_history(NULL);
2285 | return 0;
2286 | }
2287 |
2288 | /* Unit conversion */
2289 | if (argc - optind == 2)
2290 | if (convertunit(argv[optind], argv[optind + 1], sectorsz) == -1)
2291 | return -1;
2292 |
2293 | /*Arithmetic operation*/
2294 | if (argc - optind == 1) {
2295 | if (cfg.bcmode)
2296 | return try_bc(argv[optind]);
2297 |
2298 | curexpr = argv[optind];
2299 | return evaluate(argv[optind], sectorsz);
2300 | }
2301 |
2302 | return -1;
2303 | }
2304 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | '''
4 | bcal (Byte CALculator) test script
5 |
6 | Author: Arun Prakash Jana
7 | Email : engineerarun@gmail.com
8 | Home : https://github.com/jarun/bcal
9 |
10 | NOTES:
11 |
12 | 1. Use the `-l` (light mode) option to print and
13 | verify against minimal output in decimal bytes
14 |
15 | 2. Before raising a PR,
16 | a. add relevant test cases
17 | b. run `make test`
18 | '''
19 |
20 | import pytest
21 | import subprocess
22 |
23 | test = [
24 | ('./bcal', '-m', '10', 'mb'), # 0
25 | ('./bcal', '-m', '10', 'TiB'), # 1
26 | ('./bcal', '-m', '.1', ' KIb '), # 2
27 | ('./bcal', '-m', '.1', 'KB '), # 3
28 | ('./bcal', '-m', '10', 'lb'), # 4
29 | ('./bcal', '-m', "0x4 kb * 2 + 5 mib"), # 5
30 | ('./bcal', '-m', "5*5*5*5 mIB"), # 6
31 | ('./bcal', '-m', "5mb*5*5*5"), # 7
32 | ('./bcal', '-m', "5 tb / 12"), # 8
33 | ('./bcal', '-m', "2kb+3mb/4*5+5*56mb"), # 9
34 | ('./bcal', '-m', "( 5 * 3) * (4 * 7 b)"), # 10
35 | ('./bcal', '-m', "( 5 * 3 + 8) * (4 * 7 b)"), # 11
36 | ('./bcal', '-m', "( 5 * (3 + 8 )) * (4 * 7 b )"), # 12
37 | ('./bcal', '-m', "( 5 ) * 2 mib"), # 13
38 | ('./bcal', '-m', "3 mb - 2 mib"), # 14
39 | ('./bcal', '-m', "2mb-3mib"), # 15
40 | ('./bcal', '-m', "2 mib * -2"), # 16
41 | ('./bcal', '-m', "2mib*-2"), # 17
42 | ('./bcal', '-m', "2giB*2/2"), # 18
43 | ('./bcal', '-m', "1miB / 4 kib"), # 19
44 | ('./bcal', '-m', "(2giB*2)/2kib"), # 20
45 | ('./bcal', '-m', "1b / 0"), # 21
46 | ('./bcal', '-m', "2qB*2"), # 22
47 | ('./bcal', '-m', "((2giB)*2/2)"), # 23
48 | ('./bcal', '-m', "((2giB)*(2/2)"), # 24
49 | ('./bcal', '-m', "((2giB)*1)/(2/2))"), # 25
50 | ('./bcal', '-m', "((2giB)*1)/(2/2)"), # 26
51 | ('./bcal', '-m', "(((2giB)*)2/2)"), # 27
52 | ('./bcal', '-m', "(2giB)*2*"), # 28
53 | ('./bcal', '-m', '0b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', 'b'), # 29
54 | ('./bcal', '-m', '0xffffffffffffffffffffffffffffffff', 'b'), # 30
55 | ('./bcal', '-m', '340282366920938463463374607431768211455', 'b'), # 31
56 | ('./bcal', '-m', '0xffffffffffffffff', 'b'), # 32
57 | ('./bcal', '-m', "2b / 3"), # 33
58 | ('./bcal', '-m', "2 kIb/((3 ) )"), # 34
59 | ('./bcal', '-m', "2 gIb/ - 3"), # 35
60 | ('./bcal', '-m', "(2) kIb/((3))"), # 36
61 | ('./bcal', '-m', "(2) 4 kIb/((3))"), # 37
62 | ('./bcal', '-m', "(2) 4 kIb/((3))(2)"), # 38
63 | ('./bcal', '-m', "2 / 3 tib "), # 39
64 | ('./bcal', '-m', " 1000 "), # 40
65 | ('./bcal', '-m', " 0x1234mib "), # 41
66 | ('./bcal', '-m', " "), # 42
67 | ('./bcal', '-m', "0x18mb"), # 43
68 | ('./bcal', '-m', "0x18mb", "kb"), # 44
69 | ('./bcal', '-m', "0x18mb82"), # 45
70 | ('./bcal', '-m', "0x18mbc4"), # 46
71 | ('./bcal', '-m', "0x18mb 82"), # 47
72 | ('./bcal', '-m', "0x18mb", "82"), # 48
73 |
74 | ('./bcal', "-c", "0b"), # 49
75 | ('./bcal', "-c", "0x"), # 50
76 | ('./bcal', "-c", "0"), # 51
77 | ('./bcal', "-c", "0b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), # 52
78 | ('./bcal', "-c", "0xffffffffffffffffffffffffffffffff"), # 53
79 | ('./bcal', "-c", "340282366920938463463374607431768211455"), # 54
80 | ('./bcal', "-c", "0b1111111111111111111111111111111111111111111111111111111111111111"), # 55
81 | ('./bcal', "-c", "0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), # 56
82 | ('./bcal', '-m', "1kib/ 4kb"), # 57
83 | ('./bcal', '-m', "0kib /4kb"), # 58
84 | ('./bcal', '-m', "{10+2"), # 59
85 | ('./bcal', '-m', "10 + 2]"), # 60
86 | ('./bcal', '-m', "2 >>> 2"), # 61
87 | ('./bcal', '-m', "2 b<3"), # 62
88 | ('./bcal', '-m', "(2giB * 2) / (2kib >> 2)"), # 63
89 | ('./bcal', '-b', "9876543210.987654321 * 123456789.123456789"), # 64
90 | ('./bcal', "4 + 3 - 2 + 10 * 5 / 2 + 7 - 6 + 9 - 10 / 5 * 2"), # 65
91 | ('./bcal', '-m', "4b + 3b - 2b + 10b * 5 / 2 + 7b - 6b + 9b - 10b / 5 * 2"), # 66
92 | ('./bcal', '-m', "0xff / 0xf + 1337"), # 67
93 | ('./bcal', '-m', "(0xff giB * 2) / (2kib >> 2)"), # 68
94 | ('./bcal', '-m', "(2.2giB * 2) / (2.2kib >> 2)"), # 69
95 | ('./bcal', '-m', "0xbb b * 2"), # 70
96 | ('./bcal', '-m', "0xbb * 2"), # 71
97 | ('./bcal', '-b', "(50,000 - 2,000) * 1,500"), # 72
98 | ]
99 |
100 | res = [
101 | b'10000000 B\n', # 0
102 | b'10995116277760 B\n', # 1
103 | b'102 B\n', # 2
104 | b'100 B\n', # 3
105 | b'ERROR: unknown unit\n', # 4
106 | b'5250880 B\n', # 5
107 | b'655360000 B\n', # 6
108 | b'625000000 B\n', # 7
109 | b'WARNING: result truncated\n416666666666 B\n', # 8
110 | b'283752000 B\n', # 9
111 | b'420 B\n', # 10
112 | b'644 B\n', # 11
113 | b'1540 B\n', # 12
114 | b'10485760 B\n', # 13
115 | b'902848 B\n', # 14
116 | b'ERROR: negative result\n', # 15
117 | b'ERROR: negative token\n', # 16
118 | b'ERROR: negative token\n', # 17
119 | b'2147483648 B\n', # 18
120 | b'256\n', # 19
121 | b'2097152\n', # 20
122 | b'ERROR: division by 0\n', # 21
123 | b'ERROR: unknown unit\n', # 22
124 | b'2147483648 B\n', # 23
125 | b'ERROR: unbalanced expression\n', # 24
126 | b'ERROR: unbalanced expression\n', # 25
127 | b'2147483648 B\n', # 26
128 | b'ERROR: invalid token\n', # 27
129 | b'ERROR: invalid token\n', # 28
130 | b'340282366920938463463374607431768211455 B\n', # 29
131 | b'340282366920938463463374607431768211455 B\n', # 30
132 | b'340282366920938463463374607431768211455 B\n', # 31
133 | b'18446744073709551615 B\n', # 32
134 | b'WARNING: result truncated\n0 B\n', # 33
135 | b'WARNING: result truncated\n682 B\n', # 34
136 | b'ERROR: negative token\n', # 35
137 | b'ERROR: invalid expression\n', # 36
138 | b'WARNING: result truncated\nERROR: invalid expression\n', # 37
139 | b'WARNING: result truncated\nERROR: invalid expression\n', # 38
140 | b'ERROR: unit mismatch in /\n', # 39
141 | b'1000 B\n', # 40
142 | b'4886364160 B\n', # 41
143 | b'ERROR: invalid value\n', # 42
144 | b'24000000 B\n', # 43
145 | b'ERROR: malformed input\n', # 44
146 | b'ERROR: malformed input\n', # 45
147 | b'ERROR: malformed input\n', # 46
148 | b'ERROR: malformed input\n', # 47
149 | b'ERROR: unknown unit\n', # 48
150 |
151 | b'ERROR: invalid input\n\n', # 49
152 | b'ERROR: invalid input\n\n', # 50
153 | b' (b) 0\n (d) 0\n (h) 0x0\n\n', # 51
154 | b' (b) 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111\n (d) 340282366920938463463374607431768211455\n (h) 0xffffffffffffffffffffffffffffffff\n\n', # 52
155 | b' (b) 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111\n (d) 340282366920938463463374607431768211455\n (h) 0xffffffffffffffffffffffffffffffff\n\n', # 53
156 | b' (b) 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111\n (d) 340282366920938463463374607431768211455\n (h) 0xffffffffffffffffffffffffffffffff\n\n', # 54
157 | b' (b) 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111\n (d) 18446744073709551615\n (h) 0xffffffffffffffff\n\n', # 55
158 | b'ERROR: invalid input\n\n', # 56
159 | b'WARNING: result truncated\n0\n', # 57
160 | b'0\n', # 58
161 | b'ERROR: first brackets only\n', # 59
162 | b'ERROR: first brackets only\n', # 60
163 | b'ERROR: invalid sequence >>>\n', # 61
164 | b'ERROR: invalid operator <\n', # 62
165 | b'8388608\n', # 63
166 | b'1219326312467611632.3609205901\n', # 64
167 | b'36\n', # 65
168 | b'36 B\n', # 66
169 | b'1354\n', # 67
170 | b'1069547520\n', # 68
171 | b'WARNING: result truncated\n8391587\n', # 69
172 | b'374 B\n', # 70
173 | b'374\n', # 71
174 | b'72000000\n', # 72
175 | ]
176 |
177 |
178 | @pytest.mark.parametrize('item, res', zip(test, res))
179 | def test_output(item, res):
180 | try:
181 | out = subprocess.check_output(item, stderr=subprocess.STDOUT)
182 | except subprocess.CalledProcessError as e:
183 | # print(e.output)
184 | assert e.output == res
185 | else:
186 | assert out == res
187 |
--------------------------------------------------------------------------------