Login
Newsletter
Werbung

Mo, 17. Juli 2000, 00:00

GNU Readline Bibliothek

Problematischer sind die High-Level-Routinen. Man kann natürlich mit sscanf() aus dem eingelesenen String etwas lesen, doch wissen wir nicht, an welcher Stelle wir uns danach in der bereits eingelesenen Zeile befinden, da die Funktionen der scanf()-Familie nicht zurückgeben, wieviele Zeichen sie gelesen haben. Das bedeutet, dass wir Code schreiben müssten, der herausfindet, bis zu welchem Punkt sscanf() wohl gelesen hat. Da wir als Eingabe nur Ganzzahlen erlauben, ist das in unserem Fall hier kein unlösbares Problem, aber ich denke, dass die meisten Leute Besseres zu tun haben als Code zu schreiben, der eine Gleitkommazahl (evtl. noch in wissenschaftlicher Notation) parsen kann.

Ansonsten ist an der neuen Version des Codes nichts aufregendes. Compilieren können Sie ihn mit

gcc -W -g -O2 -lreadline -o rlcalc rlcalc.c

Hier nun der komplette Quellcode und im Anschluss daran ein entsprechender Patch, durch den nochmal deutlich wird, was geändert wurde.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <readline/readline.h>
#include <readline/history.h>
void err (const char *s)
{
 fprintf (stderr, "%s\n", s);
 exit (EXIT_FAILURE);
}
char *s = NULL;
int pos;
int next_char (void)
{
 int c;
 if (s != NULL)
 {
 /* Keine neue Zeile lesen */
 c = s[++pos];
 if (c == '\0')
 {
 c = '\n';
 free (s);
 s = NULL;
 }
 return c;
 }
 else
 {
 /* Einlesen einer Zeile erforderlich */
 do
 {
 if (s != NULL)
 free (s);
 s = readline ("> ");
 if (s == NULL)
 return EOF;
 }
 while (s[0] == '\0');
 add_history (s);
 pos = 0;
 return s[0];
 }
}
/* Einen Ausdruck einlesen und auswerten */
double expr (int eof_is_okay)
{
 int c;
 char buf[32];
 /* Ein brauchbares Zeichen einlesen */
 do
 {
 c = next_char ();
 if (c == EOF)
 {
 if (!eof_is_okay)
 err ("Unerwartetes Dateiende");
 else
 {
 putchar ('\n');
 exit (EXIT_SUCCESS);
 }
 }
 }
 while (isspace (c));
 switch (c)
 {
 case '+': return expr (0) + expr (0);
 case '*': return expr (0) * expr (0);
 case '-': return expr (0) - expr (0);
 case '/':
 {
 double first = expr (0);
 double second = expr (0);
 if (second == 0.0)
 err ("Division durch Null");
 return first / second;
 }
 default:
 {
 int i;
 if (!isdigit (c))
 break;
 /* Es ist eine Zahl */
 sscanf (&s[pos], "%d", &i);
 /* Neue Position ermitteln */
 while (isdigit (s[pos + 1]))
 pos++;
 return (double) i;
 }
 }
 sprintf (buf, "Unbekanntes Zeichen: `%c'", c);
 err (buf);
 return 0.0;
}

int main (void)
{
 rl_bind_key ('\t', rl_insert);
 while (1)
 printf ("%lf\n", expr (1));
 return 0;
}

Und wie versprochen, hier der Patch (einen Patch erstellt man übrigens mit diff --unified datei1 datei2 > datei.patch):

--- calc.c Wed Jun 21 20:36:57 2000
+++ rlcalc.c Wed Jun 21 20:10:49 2000
@@ -1,6 +1,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <readline/readline.h>
+#include <readline/history.h>
 void
 err (const char *s)
@@ -9,6 +11,44 @@
 exit (EXIT_FAILURE);
 }
+char *s = NULL;
+int pos;
+
+int
+next_char (void)
+{
+ int c;
+
+ if (s != NULL)
+ {
+ /* Es muss keine neue Zeile gelesen werden */
+ c = s[++pos];
+ if (c == '\0')
+ {
+ c = '\n';
+ free (s);
+ s = NULL;
+ }
+ return c;
+ }
+ else
+ {
+ /* Einlesen einer Zeile erforderlich */
+ do
+ {
+ if (s != NULL)
+ free (s);
+ s = readline ("> ");
+ if (s == NULL)
+ return EOF;
+ }
+ while (s[0] == '\0');
+ add_history (s);
+ pos = 0;
+ return s[0];
+ }
+}
+
 /* Einen Ausdruck einlesen und auswerten */
 double
 expr (int eof_is_okay)
@@ -19,13 +59,16 @@
 /* Ein brauchbares Zeichen einlesen */
 do
 {
- c = getchar ();
- if (feof (stdin))
+ c = next_char ();
+ if (c == EOF)
 {
 if (!eof_is_okay)
 err ("Unerwartetes Dateiende");
 else
- exit (EXIT_SUCCESS);
+ {
+ putchar ('\n');
+ exit (EXIT_SUCCESS);
+ }
 }
 }
 while (isspace (c));
@@ -52,8 +95,10 @@
 if (!isdigit (c))
 break;
 /* Es ist eine Zahl */
- ungetc (c, stdin);
- scanf ("%d", &i);
+ sscanf (&s[pos], "%d", &i);
+ /* Neue Position ermitteln */
+ while (isdigit (s[pos + 1]))
+ pos++;
 return (double) i;
 }
 }
@@ -65,6 +110,7 @@
 int
 main (void)
 {
+ rl_bind_key ('\t', rl_insert);
 while (1)
 printf ("%lf\n", expr (1));

Zuletzt sollte noch erwähnt werden, dass die GNU Readline-Bibliothek im Gegensatz zu vielen anderen Bibliotheken nicht der GNU Lesser General Public License, sondern der "normalen" GNU General Public License unterliegt. Dadurch soll verhindert werden, dass sie von nicht-freier Software benutzt werden kann.

Kommentare (Insgesamt: 0 )
Pro-Linux
Pro-Linux @Facebook
Neue Nachrichten
Werbung