1/*
2 * implement the "dc" Desk Calculator language.
3 *
4 * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can either send email to this
18 * program's author (see below) or write to:
19 *   The Free Software Foundation, Inc.
20 *   59 Temple Place, Suite 330
21 *   Boston, MA 02111 USA
22 */
23
24/* Written with strong hiding of implementation details
25 * in their own specialized modules.
26 */
27/* This module contains the argument processing/main functions.
28 */
29
30#include "config.h"
31
32#include <stdio.h>
33#ifdef HAVE_STDLIB_H
34# include <stdlib.h>
35#endif
36#ifdef HAVE_STRING_H
37# include <string.h>
38#else
39# ifdef HAVE_STRINGS_H
40#  include <strings.h>
41# endif
42#endif
43#include <getopt.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <errno.h>
47#include "dc.h"
48#include "dc-proto.h"
49
50#ifndef EXIT_SUCCESS	/* C89 <stdlib.h> */
51# define EXIT_SUCCESS	0
52#endif
53#ifndef EXIT_FAILURE	/* C89 <stdlib.h> */
54# define EXIT_FAILURE	1
55#endif
56
57const char *progname;	/* basename of program invocation */
58
59static void
60bug_report_info DC_DECLVOID()
61{
62	printf("Email bug reports to:  bug-dc@gnu.org .\n");
63}
64
65static void
66show_version DC_DECLVOID()
67{
68	printf("dc (GNU %s %s) %s\n", PACKAGE, VERSION, DC_VERSION);
69	printf("\n%s\n\
70This is free software; see the source for copying conditions.  There is NO\n\
71warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n\
72to the extent permitted by law.\n", DC_COPYRIGHT);
73}
74
75/* your generic usage function */
76static void
77usage DC_DECLARG((f))
78	FILE *f DC_DECLEND
79{
80	fprintf(f, "\
81Usage: %s [OPTION] [file ...]\n\
82  -e, --expression=EXPR    evaluate expression\n\
83  -f, --file=FILE          evaluate contents of file\n\
84  -h, --help               display this help and exit\n\
85  -V, --version            output version information and exit\n\
86\n\
87", progname);
88	bug_report_info();
89}
90
91/* returns a pointer to one past the last occurance of c in s,
92 * or s if c does not occur in s.
93 */
94static char *
95r1bindex DC_DECLARG((s, c))
96	char *s DC_DECLSEP
97	int  c DC_DECLEND
98{
99	char *p = strrchr(s, c);
100
101	if (!p)
102		return s;
103	return p + 1;
104}
105
106static void
107try_file(const char *filename)
108{
109	FILE *input;
110
111	if (strcmp(filename, "-") == 0) {
112		input = stdin;
113	} else {
114		struct stat sb;
115		if (stat(filename, &sb) < 0) {
116			fprintf(stderr, "Cannot stat %s: %s\n",
117				filename, strerror(errno));
118			exit(EXIT_FAILURE);
119		}
120		if (S_ISDIR(sb.st_mode)) {
121			fprintf(stderr, "Cannot use directory as input!\n");
122			exit(EXIT_FAILURE);
123		}
124		if ( !(input=fopen(filename, "r")) ) {
125			fprintf(stderr, "Could not open file ");
126			perror(filename);
127			exit(EXIT_FAILURE);
128		}
129	}
130	if (dc_evalfile(input))
131		exit(EXIT_FAILURE);
132	if (input != stdin)
133		fclose(input);
134}
135
136
137int
138main DC_DECLARG((argc, argv))
139	int  argc DC_DECLSEP
140	char **argv DC_DECLEND
141{
142	static struct option const long_opts[] = {
143		{"expression", required_argument, NULL, 'e'},
144		{"file", required_argument, NULL, 'f'},
145		{"help", no_argument, NULL, 'h'},
146		{"version", no_argument, NULL, 'V'},
147		{NULL, 0, NULL, 0}
148	};
149	int did_eval = 0;
150	int c;
151
152	progname = r1bindex(*argv, '/');
153#ifdef HAVE_SETVBUF
154	/* attempt to simplify interaction with applications such as emacs */
155	(void) setvbuf(stdout, NULL, _IOLBF, 0);
156#endif
157	dc_math_init();
158	dc_string_init();
159	dc_register_init();
160	dc_array_init();
161
162	while ((c = getopt_long(argc, argv, "hVe:f:", long_opts, (int *)0)) != EOF) {
163		switch (c) {
164		case 'e':
165			{	dc_data string = dc_makestring(optarg, strlen(optarg));
166				if (dc_evalstr(string))
167					return EXIT_SUCCESS;
168				dc_free_str(&string.v.string);
169				did_eval = 1;
170			}
171			break;
172		case 'f':
173			try_file(optarg);
174			did_eval = 1;
175			break;
176		case 'h':
177			usage(stdout);
178			return EXIT_SUCCESS;
179		case 'V':
180			show_version();
181			return EXIT_SUCCESS;
182		default:
183			usage(stderr);
184			return EXIT_FAILURE;
185		}
186	}
187
188	for (; optind < argc; ++optind) {
189		try_file(argv[optind]);
190		did_eval = 1;
191	}
192	if (!did_eval) {
193		/* if no -e commands and no command files, then eval stdin */
194		if (dc_evalfile(stdin))
195			return EXIT_FAILURE;
196	}
197	return EXIT_SUCCESS;
198}
199