1 /* @(#)calc.c	1.27 21/08/20 Copyright 1985-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)calc.c	1.27 21/08/20 Copyright 1985-2021 J. Schilling";
6 #endif
7 /*
8  *	Simples Taschenrechnerprogramm
9  *
10  *	Copyright (c) 1985-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/stdlib.h>
28 #include <schily/utypes.h>
29 #include <schily/standard.h>
30 #define	GT_COMERR		/* #define comerr gtcomerr */
31 #define	GT_ERROR		/* #define error gterror   */
32 #include <schily/schily.h>
33 #ifdef	FERR_DEBUG
34 #include <schily/termios.h>
35 #endif
36 #include <schily/nlsdefs.h>
37 
38 #define	LLEN	100
39 
40 #define	LSHIFT	1000
41 #define	RSHIFT	1001
42 
43 LOCAL	void	usage	__PR((int));
44 EXPORT	int	main	__PR((int, char **));
45 LOCAL	void	prdig	__PR((int));
46 LOCAL	void	prlldig	__PR((Llong));
47 LOCAL	void	kommentar __PR((void));
48 LOCAL	int	xbreakline __PR((char *, char *, char **, int));
49 
50 LOCAL void
usage(exitcode)51 usage(exitcode)
52 	int	exitcode;
53 {
54 	error("Usage: calc [options]\n");
55 	error("Options:\n");
56 	error("	-help	Print this help.\n");
57 	error("	-version Print version number.\n");
58 	kommentar();
59 	exit(exitcode);
60 	/* NOTREACHED */
61 }
62 
63 EXPORT int
main(ac,av)64 main(ac, av)
65 	int	ac;
66 	char	**av;
67 {
68 	int	cac;
69 	char	* const* cav;
70 	BOOL	help = FALSE;
71 	BOOL	prversion = FALSE;
72 	char	eingabe[LLEN+1];
73 	char	*argumente[8];
74 	int	i;
75 	int	op = 0;
76 	char	*opstr;
77 	Llong	arg1;
78 	Llong	arg2;
79 	Llong	ergebnis;
80 	Llong	rest = (Llong)0;
81 	int	iarg1;
82 	int	iarg2;
83 	int	iergebnis;
84 	int	irest = 0;
85 
86 	save_args(ac, av);
87 
88 	(void) setlocale(LC_ALL, "");
89 
90 #ifdef  USE_NLS
91 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
92 #define	TEXT_DOMAIN "calc"	/* Use this only if it weren't */
93 #endif
94 	{ char	*dir;
95 	dir = searchfileinpath("share/locale", F_OK,
96 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
97 	if (dir)
98 		(void) bindtextdomain(TEXT_DOMAIN, dir);
99 	else
100 #if defined(PROTOTYPES) && defined(INS_BASE)
101 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
102 #else
103 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
104 #endif
105 	(void) textdomain(TEXT_DOMAIN);
106 	}
107 #endif 	/* USE_NLS */
108 
109 	cac = --ac;
110 	cav = ++av;
111 	if (getallargs(&cac, &cav, "help,version", &help, &prversion) < 0) {
112 		errmsgno(EX_BAD, "Bad Option %s.\n", cav[0]);
113 		usage(EX_BAD);
114 	}
115 	if (help)
116 		usage(0);
117 	if (prversion) {
118 		gtprintf("Calc release %s %s (%s-%s-%s) Copyright (C) 1985, 89-91, 1996, 2000-2021 %s\n",
119 				"1.27", "2021/08/20",
120 				HOST_CPU, HOST_VENDOR, HOST_OS,
121 				_("J�rg Schilling"));
122 		exit(0);
123 	}
124 
125 	putchar('?'); flush();
126 	while (getline(eingabe, LLEN) >= 0 && !streql(eingabe, "quit")) {
127 
128 		opstr = eingabe;
129 		while (*opstr == ' ' || *opstr == '\t')
130 			opstr++;
131 
132 		/*
133 		 * optional Kommentarausgabe
134 		 */
135 		if (streql(opstr, "help")) {
136 			kommentar();
137 			putchar('?'); flush();
138 			continue;
139 		}
140 
141 		/*
142 		 * Test des Formats und der Argumente
143 		 */
144 		i = xbreakline(opstr, " \t", argumente, 5);
145 		if (*argumente[i-1] == '\0')
146 			i--;
147 		switch (i) {
148 
149 		case 1:
150 			if (*astoll(argumente[0], &ergebnis) != '\0') {
151 				gtprintf("'%s' ist keine Zahl!\n?", argumente[0]);
152 				continue;
153 			}
154 			iergebnis	= (int)ergebnis;
155 			goto print;
156 		case 2:
157 			op = *argumente[0];
158 			if (op != '!' && op != '~') {
159 				gtprintf("Unzul�ssiger Operator f�r: ");
160 				gtprintf("'op argument1'\n?");
161 				continue;
162 			}
163 			if (*astoll(argumente[1], &arg1) != '\0') {
164 				gtprintf("'%s' ist keine Zahl!\n?", argumente[1]);
165 				continue;
166 			}
167 			break;
168 		case 3:
169 			if (*astoll(argumente[0], &arg1) != '\0') {
170 				gtprintf("'%s' ist keine Zahl!\n?", argumente[0]);
171 				continue;
172 			}
173 			if (*astoll(argumente[2], &arg2) != '\0') {
174 				gtprintf("'%s' ist keine Zahl!\n?", argumente[2]);
175 				continue;
176 			}
177 			break;
178 
179 		default:
180 			gtprintf("Die Eingabe hat nicht das richtige Format: ");
181 			gtprintf("'argument1 op argument2'\n?");
182 			continue;
183 		}
184 
185 		/*
186 		 * Test der Operationssymbole
187 		 */
188 		op = 0;
189 		opstr = argumente[1];
190 		if (i == 2)
191 			opstr = argumente[0];
192 
193 		if (streql(opstr, "<<")) {
194 			op = LSHIFT;
195 		} else if (streql(opstr, ">>")) {
196 			op = RSHIFT;
197 		} else if (opstr[1] != '\0') {
198 			gtprintf("Operationssymbole sind einstellig. Falsche Eingabe!\n?");
199 			continue;
200 		} else if (!op) {
201 			op = *opstr;
202 		}
203 
204 		i = 0;
205 		iergebnis	= (int)ergebnis;
206 		iarg1		= (int)arg1;
207 		iarg2		= (int)arg2;
208 
209 		switch (op) {
210 
211 		case '+':
212 			ergebnis = arg1 + arg2;
213 			iergebnis = iarg1 + iarg2;
214 			break;
215 		case '-':
216 			ergebnis = arg1 - arg2;
217 			iergebnis = iarg1 - iarg2;
218 			break;
219 		case '*':
220 			ergebnis = arg1 * arg2;
221 			iergebnis = iarg1 * iarg2;
222 			break;
223 		case LSHIFT:
224 			ergebnis = (Ullong)arg1 << arg2;
225 			iergebnis = (unsigned)iarg1 << iarg2;
226 			break;
227 		case RSHIFT:
228 			ergebnis = (Ullong)arg1 >> arg2;
229 			iergebnis = (unsigned)iarg1 >> iarg2;
230 			break;
231 		case '^':
232 			ergebnis = arg1 ^ arg2;
233 			iergebnis = iarg1 ^ iarg2;
234 			break;
235 		case '&':
236 			ergebnis = arg1 & arg2;
237 			iergebnis = iarg1 & iarg2;
238 			break;
239 		case '|':
240 			ergebnis = arg1 | arg2;
241 			iergebnis = iarg1 | iarg2;
242 			break;
243 		case '!':
244 			ergebnis = !arg1;
245 			iergebnis = !iarg1;
246 			break;
247 		case '~':
248 			ergebnis = ~arg1;
249 			iergebnis = ~iarg1;
250 			break;
251 		case '%':
252 		case '/': if (arg2 == 0) {
253 				gtprintf("Division durch Null ist unzulaessig.\n?");
254 				i = 1;
255 				break;
256 			} else {
257 				/*
258 				 * 9223372036854775808 /  322122547200
259 				 * liefert eine Integer(32) Division durch 0
260 				 */
261 				ergebnis = arg1 / arg2;
262 				rest = arg1 % arg2;
263 				if (iarg2 == 0) {
264 					/*
265 					 * Alle unteren 32 Bit sind 0
266 					 * Division durch Null verhindern.
267 					 */
268 					iergebnis = irest = 0;
269 				} else {
270 					iergebnis = iarg1 / iarg2;
271 					irest = iarg1 % iarg2;
272 				}
273 
274 				if (op == '%') {
275 					ergebnis = rest;
276 					iergebnis = irest;
277 				}
278 				break;
279 			}
280 
281 		default:
282 			gtprintf("Unzulaessiger Operator!\n?");
283 			i = 1;
284 			break;
285 		}
286 		if (i == 1)
287 			continue;
288 
289 print:
290 		/*
291 		 * Ausgabe
292 		 */
293 		prdig(iergebnis);
294 		if (op == '/') {
295 			gtprintf("\nRest (dezimal): %d\n", irest);
296 			prdig(irest);
297 		}
298 		putchar('\n');
299 
300 		prlldig(ergebnis);
301 		if (op == '/') {
302 			gtprintf("\nRest (dezimal): %lld\n", rest);
303 			prlldig(rest);
304 		}
305 		putchar('\n');
306 
307 		putchar('?'); flush();
308 	}
309 	if (ferror(stdin)) {
310 #ifdef	FERR_DEBUG
311 		pid_t	pgrp;
312 		ioctl(STDIN_FILENO, TIOCGPGRP, (char *)&pgrp);
313 		errmsg("Read error on stdin. pid %ld pgrp %ld tty pgrp %ld\n",
314 			(long)getpid(), (long)getpgid(0), (long)pgrp);
315 #else
316 		errmsg("Read error on stdin.\n");
317 #endif
318 	}
319 	return (0);
320 }
321 
322 LOCAL void
prdig(n)323 prdig(n)
324 	int	n;
325 {
326 	register int	i;
327 
328 	printf(" = %d   %u   0%o   0x%x\n = ", n, n, n, n);
329 	if (n < 0)
330 		putchar('1');
331 	else
332 		putchar('0');
333 	for (i = 1; i <= 31; i++) {
334 		/*
335 		 * New compilers like to make left shifting signed vars illegal
336 		 */
337 		n = (int)(((unsigned int)n) << 1);
338 		if (n < 0)
339 			putchar('1');
340 		else
341 			putchar('0');
342 		if (i%4 == 3)
343 			putchar(' ');
344 	}
345 }
346 
347 LOCAL void
prlldig(n)348 prlldig(n)
349 	Llong	n;
350 {
351 	register int	i;
352 
353 	printf(" = %lld   %llu   0%llo   0x%llx\n = ", n, n, n, n);
354 	if (n < 0)
355 		putchar('1');
356 	else
357 		putchar('0');
358 	for (i = 1; i <= 63; i++) {
359 		/*
360 		 * New compilers like to make left shifting signed vars illegal
361 		 */
362 		n = (Llong)(((ULlong)n) << 1);
363 		if (n < 0)
364 			putchar('1');
365 		else
366 			putchar('0');
367 		if (i%4 == 3)
368 			putchar(' ');
369 	}
370 }
371 
372 LOCAL void
kommentar()373 kommentar()
374 {
375 	error("                    Taschenrechnerprogramm\n");
376 	error("                    ======================\n");
377 	error("Das Programm wird verlassen durch die Eingabe: 'QUIT'\n");
378 	error("Es kann jeweils eine bin�re Operation aus {+,-,*,/,%%,<<,>>,^,&,|}\n");
379 	error("oder eine un�re Operation aus {~,!} ausgef�hrt werden. Eine einzelne\n");
380 	error("Zahl wird wie eine un�re Operation behandelt. Als Eingabe sind nur\n");
381 	error("integer-Werte zugelassen, aber es ist egal, ob sie dezimal, oktal\n");
382 	error("oder hexadezimal kodiert sind; auch verschiedene Kodierungen in einer\n");
383 	error("Rechnung sind zul�ssig. Die Ausgabe erfolgt dezimal, oktal,\n");
384 	error("hexadezimal und bin�r in zwei Darstellungen: 32 Bit sowie 64 Bit.\n");
385 	error("'/' liefert zus�tzlich den Rest (dezimal) bei ganzzahliger Division.\n");
386 	error("\n");
387 	error("***************************************************************\n");
388 	error("* Die Eingabe muss folgendes Format haben:                    *\n");
389 	error("*          'argument1 op argument2'  oder                     *\n");
390 	error("*          'op argument'             oder                     *\n");
391 	error("*          'argument'                                         *\n");
392 	error("***************************************************************\n");
393 }
394 
395 /*--------------------------------------------------------------------------*/
396 #include <schily/string.h>
397 
398 LOCAL int
xbreakline(buf,delim,array,len)399 xbreakline(buf, delim, array, len)
400 		char	*buf;
401 	register char	*delim;
402 	register char	*array[];
403 	register int	len;
404 {
405 	register char	*bp = buf;
406 	register char	*dp;
407 	register int	i;
408 	register int	found;
409 
410 	for (i = 0, found = 1; i < len; i++) {
411 		for (dp = bp; *dp != '\0' && strchr(delim, *dp) == NULL; dp++)
412 			;
413 
414 		array[i] = bp;
415 		if (*dp != '\0' && strchr(delim, *dp) != NULL) {
416 			*dp++ = '\0';
417 			found++;
418 		}
419 		while (*dp != '\0' && strchr(delim, *dp) != NULL)
420 			dp++;
421 		bp = dp;
422 	}
423 	if (found > len)
424 		found = len;
425 	return (found);
426 }
427