Listing: Druckeranpassung leicht gemacht...

Wem ist es noch nie so ergangen, daß er in mühevoller Kleinarbeit seinen Programmquellcode geschrieben hat und ihn dann - weil er so kompliziert geworden ist und man ihn inzwischen vielleicht selbst kaum mehr versteht - ausdrucken möchte. Programmierer können davon, glaube ich, ein Liedchen singen...


81	1b 52 02 7d* ü
82	1b 52 01 7b* é
84	1b 52 02 7b* ä
85	1b 52 01 40* à
86	1b 52 05 7d* å
87	1b 52 01 5c* ç
8a	1b 52 01 7d* è
8e	1b 52 02 5b* Ä
8f	1b 52 05 5d* Å
90	1b 52 05 40* É
91	1b 52 04 7b* æ
92	1b 52 04 5b* Æ
94	1b 52 02 7c* ö
95	1b 52 06 7c* ò
97	1b 52 06 60* ù 
99	1b 52 02 5c* ö 
9a	1b 52 02 5d* Ü 
9c	1b 52 03 23* £
9d	1b 52 08 5c* ¥
9e	1b 52 02 7e* ß 
a4	1b 52 07 5c* ñ
a5	1b 52 07 5c* Ñ 
a8	1b 52 07 5d* ¿
7b	1b 52 00 7b* {
7d	1b 52 00 7d* }
7e	1b 52 00 7e* ~
dd	1b 52 02 40* §
5b	1b 52 00 5b* [
5d	1b 52 00 5d* ]
5c	1b 52 00 5c* \
+

Wohlan und frisch zur Tat, aber wohl dem, der ein Textverarbeitungsprogramm wie zum Beispiel 'Ist Word’ besitzt. Allerdings ist diese Art des Ausdruckens auch nicht die beste Lösung, denn es dauert doch immer eine Weile, bis man die Diskette mit dem Programm gefunden hat. Besser wäre es, wenn das Programm zum Ausdrucken immer schon im Rechner sein könnte. Glücklicherweise setzen sich bei den Entwicklungssystemen wie zum Beispiel ’MEGAMAX C’ oder 'PASCAL plus von CCD’ Shells durch, die es gestatten, in einer bestimmten Programmierumgebung unter GEM zu bleiben, ohne zwischen dem Editieren und Compilieren in das ’Desktop’ wieder zurückzukehren. Aber leider gibt es beim Ausdrucken von Quellcodes meist ein Problem: In den Programmen - besonders unter C -kommen häufig geschweifte Klammern und gleichzeitig deutsche Umlaute vor. Jetzt fragt man sich, worin das Problem liegt! Ganz einfach: der Drucker hat einen Zeichenvorrat, den man entsprechend der Nationalität umschalten kann. Will man also deutsche Umlaute wie zum Beispiel ’äüöß’ etc. drucken, muß dem Drucker mitgeteilt werden, daß man seinen deutschen Zeichensatz benutzen möchte. Der amerikanische Zeichensatz besitzt keine Umlaute, allerdings hat man die Stellen, die im deutschen Zeichensatz mit diesen Umlauten belegt sind, nun mit anderen Zeichen belegt, womit wir wieder bei unserer geschweiften Klammer wären, denn genau diese liegen im amerikanischen Zeichensatz an gleicher Stelle, wo im deutschen Zeichensatz die Umlaute zu finden sind. Wohlgemerkt, wir reden vom Zeichensatz im Drucker, nicht im Rechner. Will man also folgende Zeichen drucken ("[Möglichkeit]”), dann muß ich dem Drucker mitteilen, daß er den amerikanischen Zeichensatz benutzen soll, drucke meine Klammer und teile ihm dann mit, er möge wieder auf den deutschen Zeichensatz umschalten, damit ich die Umlaute in dem Text drucken kann usw.

Das Programm, das den Text ausdruckt, muß also darauf achten, welche Zeichen zum Drucker geschickt werden, und gegebenenfalls auf den entsprechenden Zeichensatz im Drucker umschalten. Dies geschieht bei den Druckern durch sogenannte Fluchtsequenzen (Näheres entnehmen sie bitte ihrem Druckerhandbuch).

Lange Rede, kurzer Sinn: wir hätten also ganz gerne ein kleines(!) Programm, das immer da ist, wenn man einen Quellcode ausdrucken möchte, das für jeden Drucker verwendbar (universell) ist und unsere Zeichenumkodierung selbständig durchführt.

Und genau dieses Programm liegt Ihnen nun vor:

  1. Die Länge des untenstehenden Programmes hält sich in Grenzen (ca. 5 Kb).
  2. GEM bietet die Möglichkeit, sogenannte Accessories beim Starten des Computers mitzuladen, die dann über die Menüzeile immer abrufbereit sind. Dies wollen wir nutzen.
  3. Das Programm liest eine leicht zu erstellende Datei und führt entsprechend dieser dann die Umkodierung durch. Es ist also universell.

Zuerst zur Erstellung der Datei:

Wir haben als Beispiel schon eine Datei abgedruckt, die eine Anpassung an einen Epson-kompatiblen Drucker darstellt. Wie ist die Datei aufgebaut...?

Die Datei besteht aus einer Anzahl von Zeilen, die mit einem abgeschlossen werden. An erster Stelle befindet sich der Hexadezimalcode des Zeichens. Die Zahlen dahinter geben die Codes an, die anstatt dieses Zeichens gesendet werden sollen. Wenn nun zum Beispiel ein ’ä’ geschickt wird, dann steht in der Tabelle folgende Zeile:

841b 52 02 7b ★

84:
Der Hexcode des ASCIIwertes des Zeichens im Rechner

1b 52 02:
Fluchtsequenz, die den Drucker (EP-SON-kompatible!) auf deutschen Zeichensatz schaltet.

7b:
Druckercode für das Zeichen im Drucker (Eine Zeichentabelle ist bestimmt in ihrem Druckerhandbuch zu finden).

*:
Abschluß der Zeile.

Sind nun alle Zeichen mit Umkodierung eingegeben, dann wird in der letzten Zeile ein ’ + ’ eingetragen, das dem Programm mitteilt, daß die Datei zu Ende ist. Noch eine Bitte: achten Sie darauf, daß zwischen jedem Hexcode genau ein Zwischenraum bleibt, sonst läuft die Umkodierung nicht richtig.

Nachdem Sie nun verstanden haben, wie die Tabelle auf gebaut ist, wollen wir kurz auf das Prinzip des Programms eingehen: das Programm lädt die Datei ’pri.sde’, die die Tabelle mit der Umkodierung enthält, reserviert entsprechend der Länge Speicherplatz, nimmt die Umkodierung der internen Tabelle vor und gibt danach den Speicherplatz wieder frei.

Die Umkodierung: Das Programm enthält ein zweidimensionales Feld, das 256 Einträge a 8 Stellen groß ist. Soll nun wie oben der Buchstabe ’ä’ umkodiert werden, werden die Ersatzwerte in das folgende Feld eingetragen.

convert[84][0]= 4;
Im ersten Eintrag steht die Anzahl der zu sendenden Zeichen. In dem oben gezeigten Beispiel sind es vier.

convert[84][1]= 0x1b;
erster Wert: Fluchtsymbol (ESC)

convert[84][2]= 0x52;
zweiter Wert: Zeichensatzänderung

convert[84][3]= 0x02;
dritter Wert: deutscher Zeichensatz

convert[84][4]= 0x7b;
vierter Wert: Wert für ’ä’ im Drucker

Die restlichen Einträge bleiben unbenutzt. Durch dieses Schema können also bis zu sieben Werte (ein Eintrag geht für den Merker der Länge 'verloren’) pro Buchstabe gesendet werden. Sollte Ihnen das nicht genügen, so ändern Sie die Initialisierung der Variablen ’convert’ entsprechend ab. Am Anfang wird die Tabelle so gefüllt, daß in jedem Feld das Zeichen steht, welches auch gesendet werden soll. So ersparen Sie es sich, jedes Zeichen in der Datei zu definieren. Haben Sie ein Zeichen nicht definiert, dann nimmt das Programm an, Sie wollten es als normalen ASCII-Wert an den Drucker schicken.

Wenn das Programm nun einen Text zum Ausdrucken geladen hat, so druckt es die Buchstaben über einen Umweg aus. Es schaut in dem Feld ’convert’ unter dem entsprechenden Eintrag nach und druckt dann diesem entsprechend die Werte aus. So werden also bei Umlauten mehrere Zeichen an den Drucker ausgegeben, während bei nicht kodierten Zeichen einfach dieses Zeichen - also nur ein Wert — an den Drucker geschickt wird. Keine Angst! Der Druckvorgang würde nur dann wesentlich langsamer, wenn der halbe Text aus Umlauten oder Klammern bestünde.

Nun will ich noch einmal kurz auf Accessories von GEM eingehen. Dies sind Programme, die ’im Hintergrund’ neben den Hauptprogramm, meist in einer kleinen Schleife, mitlaufen und darauf warten, aktiviert zu werden. Um aktiviert werden zu können, trägt man sich in die Menüzeile des Desktops mit dem Befehl ’menu_register (ap_id,” Name”)’ ein. Dann wartet man mit dem Befehl ’evnt__mesag (puffer)’ auf eine Botschaft von GEM. Botschaften gibt es in mehreren Fällen: So zum Beispiel, wenn ein Fenster verschoben, vergrößert, verkleinert wird, oder wenn ein Eintrag im Accessory-Dropdownmenü angeklickt wurde. Die Meldung steht im ersten Eintrag des zu übergebenden Botschaftspuffers. Wir müssen also vergleichen, ob eine Accessory angeklickt wurde, indem ich puffer[0] mit der Definition AC_OPEN (in gemdefs.h) vergleiche. Zusätzlich muß ich noch den fünften Wert des Puffers mit dem Rückgabewert von ’menu_register()’ vergleichen, um sicherzugehen, daß auch wir gemeint waren und nicht eine andere Accessory (es können ja mehrere im Speicher des ST laufen!). Wenn Sie wollen, können Sie sogar von einer Accessory mehrere Einträge unterbringen. Eine Accessory, die das macht, besitzen Sie sogar: Das Kontrollfeld! Ist Ihnen schon einmal aufgefallen, daß, wenn Sie das Kontrollfeld laden, automatisch die Druckeranpassung in dem Menü zusätzlich auftaucht? Übrigens: Bitte denken Sie daran, daß Sie das Programm entsprechend den Angaben ihres Entwicklungspaketes compilieren. So müssen bei MEGAMAX-C die Datei ’acc.l’ und bei dem Digital-Research (ATARI-Entwicklungspaket) die Datei ’accstart.o’ mitgelinkt werden, sonst stürzt die Accessory schon beim Booten ab!

Nun viel Spaß, und ich hoffe, daß Sie nun keinerlei Probleme mehr mit dem Ausdrucken von Quellcodes haben und auch ein wenig mehr über die Handhabung von Accessories gelernt haben.

#include <osbind.h> 
#include <gemdefs.h>

int fd;			/*	Datei-Deskriptor */
int ret;			/*	Rückgabevariable */
char *fileadr;	/*	Adresse der Anpassungsdatei */
long length;		/*	Länge der Anpassungsdatei */
int gl_apid;		/*	Applikationsidentifikation */
unsigned char convert[255][8] ;	/*	Ersatzfeld für ursprüngliche Buchstaben */
char fname[64],pname[64],retname[65];

#define BUFLEN 16000	/*	definiere Bufferlänge, kann geändert */
					/* werden */

#define void /**/	/* Funktion	bringt keinen Wert zurück */
#define hextonum(b) ((b>47 && b<58) ? b-48:b-55)	/* Hexbuchstabe in Zahl */

void main ()
{
	int i,a;
	int msg(8);
	int menu_id;

	appl_init();
	menu_id = menu_register(gl_apid," Source Printout");

	fd=Fopen("pri.sde",0);	/* öffne Anpassungsdatei */
	if (fd<0)				/*	Fehler beim	öffnen der Datei? */
		ende();		/*	ja! Ende.... */
	length=(long)Fseek(01,fd,2);	/* Ermittle Länge der Datei */
	Fseek(01,fd,0);	/* Setze Zeiger wieder auf Anfang */
	if	(!length)	/*	Länge-0 ? */
		ende();		/*	ja! Ende.......*/
	fileadr=(char*)Malloc(length);	/* Reserviere Speicherplatz für */
					/* Anpassungsdatei */ 
	if (!fileadr)	/* Nicht genug Speicherplatz vorhanden?*/
		ende();		/*	Ja! Ende......*/
	Fread(fd,length,fileadr);	/* Lese Datei an diesen Speicherplatz */
	Fclose(fd);			/* Schließe Datei */
	for(i=0; i<255; i++)	/* Initialisierung des Ersatzfeldes */
	{
		convert[i][1]=(char)i;	/* Normaler Buchstabe - nicht geändert */
		convert[i][0]=1;			/* es wird ein Zeichen gesendet */
	}
	decode();			/* Fülle Feld mit geänderten Zeichen */

	Mfree(fileadr);	/* Gebe Speicherplatz der Datei */
					/* wieder frei */

	while(1)	/* Accessories enden nie ! */
	{
		evnt_mesag(msg);	/* Botschaft von GEM abwarten */
		if(msg[0] == AC_OPEN && msg[4] == menu_id) /* Ist unserer Eintrag an- */
		{	/*	gewählt	worden	*/
			wind_update(BEG_MCTRL);	/*	Das Anwählen der Menüleiste verhindern */
			loadprint();				/* Laden und Drucken */
			wind_update(END_MCTRL);	/* Menüleiste wieder einschalten */
		}
	}
}

void ende()	/*	Anzeige eines Fehlers */
{
	form_alert(11](Datei oder nicht genug Speicher vorhanden 1( Abbruch 1"): 
	while(1); /* Eine Accessory darf nie enden, auch wenn ein Fehler auftritt •/
}

void decode ()
{
	int i ;
	int index1, index2;
	int n,f1ag:

	i=0;
	flag=index2=1:

	while(1)
	{
		while(!isxdig(*(fileadr+i)))	/* Nicht-Hexadezimalzeichen übergehen*/
		{
			if (*(fileadr+i)=='*')	/* Ende der Zeile erreicht */
			{
				convert[indexl1][0]=index2-1; /* Anzahl der zu sendenden Zeichen */
				flag-1;		/* nächstes Zeichen legt index1 fest*/
				index2=1;	/* ab Stelle 1 füllen */
			}
			else if(*(fileadr+i)=='+')	/* Ende der Datei erreicht */
				return;	/* Verlassen der Dekodierung */
			i++;			/* nächsten Buchstaben in der Datei */
		}
		/* In der folgenden Zeile werden die beiden nächsten Buchstaben als 
		Hexadezimalzahl interpretiert und in eine Zahl umgewandelt, in dem 
		man von dem Ascii-Wert bei Buchstaben von '0' - '9' 48 und bei den
		Buchstaben 'A' - ’F' 55 subtrahiert. Vorher wird aber jedes Zeichen
		in einen Großbuchstaben umgewandelt, damit kleine wie auch große #
		Buchstaben benutzt werden können */

		n=hextonum(toupper(*(fileadr+i)))*16+hextonum(toupper(*(fileadr+i+ 1)));

		if (flag)/* Erste Zahl in der Zeile.d.h. Wert des zu ändernden Zeichens*/ 
		{
			flag=0; /* Nächste Zeichen werden in das ausgesuchte Feld übernommen*/ 
			index1=n; /* Feldindex setzen */
		}
		else /* Übernahme der neuen Ersatzzeichen */
		{
			convert[index1][index2]=n;	/* Einordnung des Wertes in das Feld */
			index2++;
										/* nächster Platz für Wert */
		}
		i+=2;	/*	Datei zwei Zeichen weiter */
	}
}

int ischar(c.valst) 
char c; 
char *valst;
{
	int f1 = 0;
		for (;*valst != '\0';valst++) /* Vergleiche Buchstaben mit der */
			f1 += (c == *valst);		/* übergebenen Zeichenkette. Wenn über-*/
		return (f1);	/*	einstimmung.	dann wird flag gesetzt. */
}

int isxdig(c) /* Ist übergebener Buchstabe ein Hexzeichen ? */ 
char c;
{
	return!ischar(c."0123456789abcdefABCDEF")): 
}

void prout(c)
unsigned char c;	/*	Buchstabe	der ausgedruckt werden soll */
{
	int i;
	for (i=1; i<-convert[c][0]; i++)
		Cprnout(convert[c][i]);
}

void loadprint()
{
	long num_bytes;	/* Anzahl der zu ladenden Bytes */
	fileadr=(char*)Malloc((long)BUFLEN); /* Buffer zum teilweisen Laden der */
	if (!fileadr) ende();		/* Datei. Da eine Datei auch sehr lang */
	/* sein kann, aber  eventuell nicht genug Speicherplatz vorhanden ist. wird*/
	/* das Laden und Drucken in Etappen a 16000 Zeichen ausgeführt */

ret=do_file(fname,pname,retname); /* Filenamen holen und aufbereiten */ 
graf_mouse(HOURGLASS,01);	/*	Maus auf 'Hummel' schalten */
if (ret)	/*	OK gedrückt */
{
	fd=Fopen(retname,0);			/•	Datei öffnen */
	length=(long)Fseek(01,fd,2);	/*	Ermittle Lange der Datei */
	Fseek(O1,fd,0);	/*	setze Zeiger wieder auf Anfang */
	while(length>0)	/*	Noch Zeichen übrig */
		if (length>(long)BUFLEN)	/*	Noch mehr als 16000 Zeichen ? */
		{
			Fread(fd.(long)BÜFLEN.fileadr);	/* Ja, dann laden wir 16000 */ 
			pr_convert((long) BUFLEN);		/*	und drucken 16000 Zeichen aus */
		}
		else		/*	Den letzten Rest laden */
			Fread (fd, length, fileadr);	/*	Lade die übrigen Zeichen */
			pr_convert(length);	/*	und drucken sie aus */
		}
		length = (long)BUFLEN;
		}
		Fclose(fd);		/*	Datei schließen */
	}
	Mfree(fileadr);		/*	Speicher wieder freigeben */
	graf_mouse(ARROW,01);	/*	Maus wieder normal */
	/* Das war's */
}

void pr_convert(len)	/*	Länge des zu druckenden Buffers */
long len;
{
	long i;
	for(i=0; i<len; i++)
		prout(fileadr[i]) ;	/* Zeichen umkodiert ausgeben */
}

int do_file(fpath,fname.fpn)
char *fpath.*fname,*fpn;
{
	int i;	/*	Zahler	*/
	int key;	/*	für	die	Rückgabe der Taste */

			/* suche den aktuellen Pathname */ 
	fpath[0] - Dgetdrv() + 'A';	/*	ermittle aktuelles Laufwerk •/

	strcpy(&fpath(11,":”);	/*	Doppelpunkt anhängen */
	Dgetpath(fpn,0);			/* ermittle aktuellen Pathname */
	strcat(fpath.fpn);		/*	kopiere Laufwerk und Pathname zusammen */
	strcat(fpath,"\*.*");

	fsel_input(fpath,fname,&key);	/* Aufruf der Fileselector Box */
	i = strlen(fpath);			/*	lösche Searchname aus Pathname */
	while(fpath[i] != '\\') 
			i--;
	fpath[++i] = '\0';
	strcpy(fpn,fpath);	/* kopiere Pathname nach fpn */
	strcat(fpn,fname);	/* und hänge Filename an */
	return(key);			/* Übergabe des Fileselector-Box Knopfes */
}

Stefan Höhn
Aus: ST-Computer 11 / 1986, Seite 20

Links

Copyright-Bestimmungen: siehe Über diese Seite