C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
iloan.c
Go to the documentation of this file.
1/** @file iloan.c
2 @brief Installment Loan Calculator
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10#include <math.h>
11#include <signal.h>
12#include <stdbool.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17
18#define FALSE 0
19#define TRUE 1
20char in_str[BUFSIZ + 1];
21
22void numbers(char *d, char *s);
23double calculate_i(double, double, double);
24double calculate_n(double, double, double);
25double calculate_pmt(double, double, double);
26double calculate_pv(double, double, double);
27int is_numeric(char *);
28void accept_str(char *s);
29char *format_currency(float);
30char *format_interest(float);
31double accept_pv();
32double accept_n();
33double accept_i();
34double accept_pmt();
35void error_press_any_key(char *);
36void ABEND(int);
37int f_pv = 0;
38int f_n = 0;
39int f_i = 0;
40int f_pmt = 0;
41bool f_quiet = false;
42
43/** @brief iloan is a trivial application to demonstrate how a command-line
44 program can be integrated into C-Menu Form with simple file, argument, or pipe
45 i-o.
46 @details Positional arguments:
47 present_value The present value of the loan (the amount borrowed).
48 number_of_payments The total number of payments to be made.
49 interest_rate The annual interest rate (as a percentage).
50 payment_amount The amount of each payment.
51 The program calculates the missing value based on the three provided
52 values. For example, if the present value, number of payments, and interest
53 rate are provided, it will calculate the payment amount. If the present value,
54 interest rate, and payment amount are provided, it will calculate the number
55 of payments, and so on.
56 The program can be used in a non-interactive way by passing the four
57 values as arguments, with the value to be calculated set to 0. For example, to
58 calculate the payment amount for a $10,000 loan with a 5% annual interest rate
59 and 60 monthly payments, you could run:
60 @code
61 iloan 10000 60 5 0
62 @endcode
63 This is proof-of-concept code, and is not intended for production use.
64 It is not designed to be robust, and does not handle all edge cases or input
65 errors. It is intended solely to demonstrate how a simple command-line program
66 can be integrated into C-Menu Form.
67 In the future, more sophisticated abstractions, such as async event
68 handlers, serialization, rpc, database, and a standardized plugin interface
69 will be integrated into C-Menu.
70 Feel free to modify and enhance this code as needed, but please do not
71 use it in production without proper testing and validation. It is provided
72 as-is without any warranty or support. Use at your own risk.
73*/
74int main(int argc, char **argv) {
75 double pv = 0, pmt = 0, i = 0, n = 0;
76 char tmp_str[BUFSIZ];
77
78 signal(SIGINT, ABEND);
79 signal(SIGQUIT, ABEND);
80 signal(SIGHUP, ABEND);
81
82 if (argc > 1 &&
83 ((strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) ||
84 argc < 4)) {
85 printf("Usage: iloan [present_value number_of_payments "
86 "interest_rate payment_amount]\n\n");
87 exit(EXIT_SUCCESS);
88 }
89 if (argc > 4) {
90 numbers(tmp_str, argv[1]);
91 sscanf(tmp_str, "%lf", &pv);
92 sscanf(argv[2], "%lf", &n);
93 sscanf(argv[3], "%lf", &i);
94 numbers(tmp_str, argv[4]);
95 sscanf(tmp_str, "%lf", &pmt);
96 if (pv != 0)
97 f_pv = 1;
98 if (n != 0)
99 f_n = 1;
100 if (i != 0)
101 f_i = 1;
102 if (pmt != 0)
103 f_pmt = 1;
104 if (f_pv + f_n + f_i + f_pmt < 3) {
106 "Error: At least three values must be greater than zero.");
107 exit(EXIT_FAILURE);
108 }
109 f_quiet = true;
110 } else {
111 if (argc != 1) {
112 printf("Usage: iloan [present_value number_of_payments "
113 "interest_rate payment_amount]\n\n");
114 exit(EXIT_FAILURE);
115 } else {
116 if (argc == 1) {
117 printf("\nInstallment Loan Calculator\n\n");
118 printf("Three of these values must be greater than 0. The "
119 "field\n");
120 printf("with a value of 0 will be calculated.\n\n");
121 while (f_pv + f_n + f_i + f_pmt < 3) {
122 if (pv == 0) {
123 pv = accept_pv();
124 if (pv != 0)
125 f_pv = 1;
126 }
127 if (n == 0) {
128 n = accept_n();
129 if (n != 0)
130 f_n = 1;
131 }
132 if (i == 0) {
133 i = accept_i();
134 if (i != 0)
135 f_i = 1;
136 }
137 if (pmt == 0) {
138 pmt = accept_pmt();
139 if (pmt != 0)
140 f_pmt = 1;
141 }
142 if (f_pv + f_n + f_i + f_pmt < 3) {
143 error_press_any_key("Error: At least three values must "
144 "be greater than zero.");
145 }
146 }
147 printf("\nYou entered:\n\n");
148 if (pv != 0)
149 printf("Present Value - - - - - -> %s\n",
151 if (n != 0)
152 printf("Number of Payments - - -> %s\n",
154 if (i != 0)
155 printf("Interest Rate - - - - - -> %s\n",
157 if (pmt != 0)
158 printf("Payment Amount - - - - -> %s\n",
160 printf("\n\nCalculation result:\n\n");
161 }
162 }
163 }
164 if (pv == 0)
165 pv = calculate_pv(n, i, pmt);
166 else if (n == 0)
167 n = calculate_n(pv, i, pmt);
168 else if (i == 0)
169 i = calculate_i(pv, n, pmt);
170 else if (pmt == 0)
171 pmt = calculate_pmt(pv, n, i);
172
173 if (f_quiet) {
174 printf("%s\n", format_currency(pv));
175 printf("%s\n", format_currency(n));
176 printf("%s\n", format_interest(i));
177 printf("%s\n", format_currency(pmt));
178 }
179 signal(SIGINT, SIG_DFL);
180 signal(SIGQUIT, SIG_DFL);
181 signal(SIGHUP, SIG_DFL);
182}
183
184double accept_pv() {
185 double pv;
186 while (1) {
187 accept_str("Present Value - - -> ");
188 if (in_str[0] == '\0') {
189 pv = 0;
190 break;
191 } else {
192 if (is_numeric(in_str)) {
193 pv = atof(in_str);
194 if (pv < 0)
195 error_press_any_key("Present Value can't be less than 0");
196 else
197 break;
198 } else
199 error_press_any_key("Present Value must be numeric");
200 }
201 }
202 return (pv);
203}
204
205double accept_n() {
206 double n;
207 while (1) {
208 accept_str("Number of Payments > ");
209 if (is_numeric(in_str)) {
210 n = atof(in_str);
211 if (n < 0)
212 error_press_any_key("Number of Payments can't be less than 0");
213 else
214 break;
215 } else
216 error_press_any_key("Number of Payments must be numeric");
217 }
218 return (n);
219}
220
221double accept_i() {
222 double i;
223 while (1) {
224 accept_str("Rate (annual) - - -> ");
225 if (in_str[0] == '\0') {
226 i = 0;
227 break;
228 } else {
229 if (is_numeric(in_str)) {
230 i = atof(in_str);
231 if (i < 0)
232 error_press_any_key("interest Rate can't be less than 0");
233 else
234 break;
235 } else
236 error_press_any_key("Interest Rate must be numeric");
237 }
238 }
239 return (i);
240}
241
242double accept_pmt() {
243 double pmt;
244 while (1) {
245 accept_str("Payment Amount - -> ");
246 if (in_str[0] == '\0') {
247 pmt = 0;
248 break;
249 } else {
250 if (is_numeric(in_str)) {
251 pmt = atof(in_str);
252 break;
253 } else
254 error_press_any_key("Payment Amount must be numeric");
255 }
256 }
257 return (pmt);
258}
259
260void error_press_any_key(char *s) {
261 printf("%s", s);
262 getc(stdin);
263 printf("\n\n");
264}
265
266double calculate_pv(double n, double i, double pmt) {
267 double i1, pv;
268
269 if (n == 0 || i == 0 || pmt == 0)
271 "3 non-zero values required to calculate Present Value");
272 i1 = i / 1200;
273 pv = pmt * (1 - pow(1 + i1, -n)) / i1;
274 if (!f_quiet)
275 printf("Present Value - - - - - -> %s\n", format_currency(pv));
276 return (pv);
277}
278
279double calculate_n(double pv, double i, double pmt) {
280 double i1, n;
281 if (pv == 0 || i == 0 || pmt == 0)
282 error_press_any_key("3 non-zero values required to calculate "
283 "Number of Payments");
284 i1 = i / 1200;
285 n = -log(1 - pv * i1 / pmt) / log(1 + i1);
286 if (!f_quiet)
287 printf("Number of Payments - - -> %s\n", format_currency(n));
288 return (n);
289}
290
291double calculate_i(double pv, double n, double pmt) {
292 double i1 = 0, i;
293 double delta = 0.0000001;
294 double xdelta;
295 double fdelta;
296 double fprimi;
297 double ffact;
298 double fi;
299 double fman;
300 double fmann;
301 if (pv == 0 || n == 0 || pmt == 0)
303 "3 non-zero values required to calculate Interest Rate");
304 ffact = pv / pmt;
305 xdelta = 0;
306 if (ffact < n) {
307 i1 = 0.0125;
308 xdelta = 0.9;
309 }
310 while (xdelta > delta) {
311 fman = 1 / (1 + i1);
312 fmann = pow(fman, n);
313 fi = ffact * i1 + fmann - 1;
314 fprimi = ffact - n * fmann * fman;
315 fdelta = fi / fprimi;
316 i1 = i1 - fdelta;
317 if (fdelta < 0)
318 xdelta = -fdelta;
319 else
320 xdelta = fdelta;
321 }
322 i = i1 * 1200;
323 if (!f_quiet)
324 printf("interest Rate - - - - - -> %s\n", format_interest(i));
325 return (i);
326}
327
328double calculate_pmt(double pv, double n, double i) {
329 double i1, pmt;
330 if (pv == 0 || n == 0 || i == 0)
332 "3 non-zero values required to calculate Payment Amount");
333 i1 = i / 1200;
334 pmt = pv * i1 / (1 - pow(1 + i1, -n));
335 if (!f_quiet)
336 printf("Payment Amount - - - - -> %s\n", format_currency(pmt));
337 return (pmt);
338}
339
340int is_numeric(char *s) {
341 char c;
342
343 while ((c = *s++) != '\0' && c != '\n')
344 if ((c < '0' || c > '9') && c != '.' && c != ',')
345 return (FALSE);
346 return (TRUE);
347}
348
349void accept_str(char *s) {
350 fprintf(stderr, "%s", s);
351 read(0, in_str, BUFSIZ);
352}
353
354char *format_currency(float a) {
355 int digit_count, left_of_point;
356 static char sstr[80];
357 static char fstr[80];
358 char *src_ptr, *dst_ptr, *sptr, *FPtr;
359
360 sprintf(sstr, "%-18.2f", a);
361 sptr = sstr;
362 src_ptr = sstr;
363 FPtr = fstr;
364 dst_ptr = fstr;
365 while (*src_ptr++ != '\0')
366 ;
367 src_ptr -= 2;
368 left_of_point = FALSE;
369 digit_count = 0;
370 while (src_ptr >= sptr) {
371 if (left_of_point) {
372 if (*src_ptr >= '0' && *src_ptr <= '9') {
373 if (digit_count == 3) {
374 *dst_ptr++ = ',';
375 digit_count = 1;
376 } else
377 digit_count++;
378 }
379 } else if (*src_ptr == '.')
380 left_of_point = TRUE;
381 *dst_ptr++ = *src_ptr--;
382 }
383 src_ptr = dst_ptr - 1;
384 dst_ptr = sptr;
385 while (src_ptr >= FPtr)
386 *dst_ptr++ = *src_ptr--;
387 while (*--dst_ptr == ' ')
388 ;
389 *++dst_ptr = '\0';
390 return (sptr);
391}
392
393char *format_interest(float a) {
394 static char sstr[80];
395 char *s;
396
397 sprintf(sstr, "%-3.5f", a);
398 s = sstr;
399 return (s);
400}
401
402void ABEND(int e) {
403 printf("ABEND: Error %d:\n", e);
404 exit(EXIT_FAILURE);
405}
406
407void numbers(char *d, char *s) {
408 while (*s != '\0') {
409 if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9'))
410 *d++ = *s++;
411 else
412 s++;
413 }
414 *d = '\0';
415}
#define BUFSIZ
Definition view.h:29
double calculate_pmt(double, double, double)
Definition iloan.c:328
char * format_interest(float)
Definition iloan.c:393
double calculate_n(double, double, double)
Definition iloan.c:279
char in_str[BUFSIZ+1]
Definition iloan.c:20
char * format_currency(float)
Definition iloan.c:354
int f_n
Definition iloan.c:38
double accept_n()
Definition iloan.c:205
void numbers(char *d, char *s)
Definition iloan.c:407
double accept_pmt()
Definition iloan.c:242
double calculate_i(double, double, double)
Definition iloan.c:291
bool f_quiet
Definition iloan.c:41
double accept_pv()
Definition iloan.c:184
int f_i
Definition iloan.c:39
void accept_str(char *s)
Definition iloan.c:349
double accept_i()
Definition iloan.c:221
int f_pv
Definition iloan.c:37
double calculate_pv(double, double, double)
Definition iloan.c:266
int f_pmt
Definition iloan.c:40
#define TRUE
Definition iloan.c:19
#define FALSE
Definition iloan.c:18
void error_press_any_key(char *)
Definition iloan.c:260
int is_numeric(char *)
Definition iloan.c:340
void ABEND(int)
Definition iloan.c:402
int main(int argc, char **argv)
Definition amort.c:34