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:
- Die LÀnge des untenstehenden Programmes hÀlt sich in Grenzen (ca. 5 Kb).
- 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.
- 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 */
}