In den letzten Jahren habe ich den Austausch von Kartenstapeln und Drucklisten v.a. über eMail realisiert. Inzwischen erscheint das eher unsinnig. Der derzeitige Ansatz ist der, Drucklisten und Magnetbänder über html zum Download anzubieten und Möglichkeiten des Upload von Kartenstapeln und Magnetbändern zu realisieren. Als Web Plattform wird DokuWiki genutzt. In dieses System einschließlich seiner Medienverwaltung sind die folgenden Funktionen eingepasst.
Drucklisten aus dem Mainframe Betriebssystem sind in zwei Hinsichten schwierig.
In der aktuellen Hercules Konfiguration wird mit
000E 1403 "|/usr/local/bin/prtspool ./spool /usr/local/bin/savepdf nosep"
erreicht, dass alle Drucklisten separiert und als getrennte PDF Dateien im Spool Verzeichnis abgelegt werden. Ein Link im Wiki Verzeichnis ermöglicht die Ansicht und das Herunterladen der Listen im Browser über Ablage für Drucklisten in der sidebar bzw. hier.
Das Programm ist im Original von Tim Pinkawa und hat die Aufgabe einen Drucklisten-Stream zu separieren. In der hier veränderten Form erwartet das Programm die Standard Separatorseiten des DOS-5/ES. Es ruft bei entsprechender Parameterisierung für jedes der Segmente, die zunächst nach /tmp gespeichert werden, ein Programm auf, welches die Weiterverarbeitung bzw. das Abspeichern an einem gewünschten Ort übernimmt. Zu gegebenen Zeitpunkt könnte für das unter VM/DOS laufende SVS 7.2 ebenfalls ein Direktdrucker eingerichtet werden, bei dem dann die originale Segmentierung benutzt werden sollte.
#include <stdio.h> #include <string.h> /* prtspool by Tim Pinkawa (http://timpinkawa.net/hercules) * Written in December 2006, released in June 2007 */ int main(int argc, char* argv[]) { if(argc != 3 && argc != 4) { printf("prtspool - a simple print spooler for emulated 1403 printers\n"); printf("By Tim Pinkawa (http://www.timpinkawa.net/hercules)\n\n"); printf("usage: %s {output_directory} [command]\n", argv[0]); return 0; } char batchname[9]; char batchtime[15]; char tmpFile [L_tmpnam]; char cmd[225] = " "; char path[250]; char jobEnd1[] = "*** DOS-50.50/CS"; char jobEnd2[] = " E N D "; char line[200]; char ss[200]; int job = 1; while(!feof(stdin)) { int endCount = 0; fgets(line, 200, stdin); if(strcmp(line, "\f") == 0) break; int i; for(i = 1; i < 200; i++) ss[i - 1] = line[i]; FILE* jobfp; tmpnam (tmpFile); jobfp = fopen(tmpFile, "w"); /*fprintf(jobfp, ss);*/ fputs(ss, jobfp); int endOfJob = 0; while(!endOfJob && !feof(stdin)) { fgets(line, 200, stdin); if( (strstr(line, jobEnd1) != NULL) && (strstr(line, jobEnd2) != NULL) ) { endCount++; } if(endCount == 10) { endOfJob = 1; /* Einsammeln Batchname und Zeit */ strncpy(batchname, line+59, 8); batchname[8]='\0'; for (i = 0; i < 8; i++) { if (batchname[i] == ' ') { batchname[i] = '\0'; } } int i=0; while (batchname[i]) { batchname[i]=tolower(batchname[i]); i++; } batchtime[0] = line[106]; batchtime[1] = line[107]; batchtime[2] = line[103]; batchtime[3] = line[104]; batchtime[4] = line[100]; batchtime[5] = line[101]; batchtime[6] = '_'; batchtime[7] = line[110]; batchtime[8] = line[111]; batchtime[9] = line[113]; batchtime[10] = line[114]; batchtime[11] = line[116]; batchtime[12] = line[117]; batchtime[13] = '\0'; } fputs(line, jobfp); /*fprintf(jobfp, line);*/ } fclose(jobfp); if(argc == 3) { snprintf(cmd, 225, "%s %s/%s_%s.pdf %s\n", argv[2], argv[1], batchtime, batchname, tmpFile); /*printf(cmd);*/ system(cmd); cmd[0] = '\0'; remove(tmpFile); fgets(line, 200, stdin); fgets(line, 200, stdin); fgets(line, 200, stdin); } job++; } }
Nach dem Separieren und vor der Umwandlung in PDF werden die Separatorseiten mit dem folgenden Einzeiler im Script savepdf
entfernt, sofern beim Aufruf nosep
als zweiter Parameter übergeben wird.
sed "s/\x0C/\x0A\x0C\x0A/"|sed "1,/\x0C/d"|tac|sed "1,/\x0C/d"|tac|sed "s/\x0A\x0C/\x0C/"
Das Programm wandelt eine Datei aus einem Textformat mit diversen Steuerzeichen in eine PDF Datei. Das Original liest zeilenweise bis jeweils Zeilenumbruch. Dies ist für Drucklisten mit übereinandergedruckten Zeilen (Unterschied zwischen CR und LF) nicht ok. Deswegen ist das Programm auf zeichenweise Verarbeitung umgestellt. Gleichzeitig sind die Font Parameter auf sinnvolle Standards angepasst.
[roman]Beim Übereinanderdrucken gleicher Zeichen werden diese (noch) nicht fett dargestellt. Dafür müsste jede Überdruckzeile um 1-2 Pixel in der Höhe oder Breite verschoben werden.
Vor dem Compilieren muss zunächst einmal das komplette Paket mit
./configure make
vorbereitet werden, weil sonst die Bibliotheken nicht aufgebaut werden.
/*---------------------------------------------------------------------------* | PDFlib - A library for generating PDF on the fly | +---------------------------------------------------------------------------+ | Copyright (c) 1997-2005 Thomas Merz and PDFlib GmbH. All rights reserved. | +---------------------------------------------------------------------------+ | | | This software is subject to the PDFlib license. It is NOT in the | | public domain. Extended versions and commercial licenses are | | available, please check http://www.pdflib.com. | | | *---------------------------------------------------------------------------*/ /* $Id: text2pdf.c,v 1.13 2005/06/08 21:03:44 tm Exp $ * * Convert text files to PDF * */ #include <stdio.h> #include <string.h> #include <stdlib.h> #if defined(__CYGWIN32__) #include <getopt.h> #elif defined(WIN32) int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; extern int optind; #elif !defined(WIN32) && !defined(MAC) #include <unistd.h> #endif #ifdef WIN32 #include <process.h> #endif #ifdef NeXT #include <libc.h> /* for getopt(), optind, optarg */ #endif #ifdef __CYGWIN32__ #include <getopt.h> /* for getopt(), optind, optarg */ #endif #if defined WIN32 || defined __DJGPP__ || \ defined __OS2__ || defined __IBMC__ || defined __IBMCPP__ || \ defined __POWERPC__ || defined __CFM68K__ || defined __MC68K__ || \ defined AS400 || defined __ILEC400__ #define READMODE "rb" #else #define READMODE "r" #endif /* Mac, Windows, and OS/2 platforms */ /* figure out whether or not we're running on an EBCDIC-based machine */ #define ASCII_A 0x41 #define PLATFORM_A 'A' #define EBCDIC_BRACKET 0x4A #define PLATFORM_BRACKET '[' #if (ASCII_A != PLATFORM_A && EBCDIC_BRACKET == PLATFORM_BRACKET) #define PDFLIB_EBCDIC #endif #include "pdflib.h" static void usage(void) { fprintf(stderr, "text2pdf - convert text files to PDF.\n"); fprintf(stderr, "(c) PDFlib GmbH and Thomas Merz 1997-2005 www.pdflib.com\n\n"); fprintf(stderr, "usage: text2pdf [options] [textfile]\n"); fprintf(stderr, "Available options:\n"); fprintf(stderr, "-e encoding font encoding to use. Common encoding names:\n"); fprintf(stderr, " winansi, macroman, ebcdic, or user-defined\n"); fprintf(stderr, " host = default encoding of this platform\n"); fprintf(stderr, "-f fontname name of font to use\n"); fprintf(stderr, "-h height page height in points\n"); fprintf(stderr, "-m margin margin size in points\n"); fprintf(stderr, "-o filename PDF output file name\n"); fprintf(stderr, "-s size font size\n"); fprintf(stderr, "-w width page width in points\n"); exit(1); } #define BUFLEN 512 int main(int argc, char *argv[]) { char buf[BUFLEN], *s; char *pdffilename = NULL; FILE *textfile = stdin; PDF *p; int opt; int font; char *fontname, *encoding; double fontsize, fontwidth; double x, y, z, width = a4_width, height = a4_height, margin = 20; char ff, nl, cr; int c; int tof; int verbosity; fontname = "Courier"; fontsize = 7; fontwidth = 4.2; encoding = "host"; nl = '\n'; ff = '\f'; cr = '\r'; verbosity = 0; while ((opt = getopt(argc, argv, "e:f:h:m:o:s:w:v")) != -1) switch (opt) { case 'v': verbosity = 1; break; case 'e': encoding = optarg; break; case 'f': fontname = optarg; break; case 'h': height = atof(optarg); if (height < 0) { fprintf(stderr, "Error: bad page height %f!\n", height); usage(); } break; case 'm': margin = atof(optarg); if (margin < 0) { fprintf(stderr, "Error: bad margin %f!\n", margin); usage(); } break; case 'o': pdffilename = optarg; break; case 's': fontsize = atof(optarg); if (fontsize < 0) { fprintf(stderr, "Error: bad font size %f!\n", fontsize); usage(); } break; case 'w': width = atof(optarg); if (width < 0) { fprintf(stderr, "Error: bad page width %f!\n", width); usage(); } break; case '?': default: usage(); } if (!strcmp(encoding, "ebcdic")) { /* form feed is 0x0C in both ASCII and EBCDIC */ nl = 0x15; } if (pdffilename == NULL) usage(); if (optind < argc) { if ((textfile = fopen(argv[optind], READMODE)) == NULL) { fprintf(stderr, "Error: cannot open input file %s.\n",argv[optind]); exit(2); } } else textfile = stdin; p = PDF_new(); if (p == NULL) { fprintf(stderr, "Error: cannot open output file %s.\n", pdffilename); exit(1); } PDF_begin_document(p, pdffilename, 0, ""); tof = 1; /* top of form*/ PDF_set_info(p, "Title", "Converted text"); PDF_set_info(p, "Creator", "text2pdf"); if ( verbosity == 1) fprintf(stderr, "s-doc\n"); x = margin; y = height - margin; z = 0; s = &buf; while ((c = fgetc(textfile)) != EOF) { if (c == ff) { if ( verbosity == 1) fprintf(stderr, "ff\n"); if (tof == 1) { if ( verbosity == 1) fprintf(stderr, "ff-begin-page\n"); PDF_begin_page_ext(p, width, height, ""); } PDF_end_page_ext(p, ""); tof = 1; y = height - margin; z = 0; continue; } if (y < margin) { /* page break necessary? */ y = height - margin; z = 0; PDF_end_page_ext(p, ""); tof = 1; } if ((tof == 1)){ if ( verbosity == 1) fprintf(stderr, "tof\n"); PDF_begin_page_ext(p, width, height, ""); font = PDF_load_font(p, fontname, 0, encoding, ""); PDF_setfont(p, font, fontsize); tof = 0; } if (c == nl) { if ( verbosity == 1) fprintf(stderr, "\n"); y -= fontsize; z = 0; continue; } if (c == cr) { z = 0; continue; } s[0] = c; s[1] = '\0'; if ( verbosity == 1) fprintf(stderr, "%c", c); PDF_set_text_pos(p, x+z, y); PDF_continue_text(p, s); z += fontwidth; tof = 0; } if (tof == 0) { PDF_end_page_ext(p, ""); } PDF_end_document(p, ""); PDF_delete(p); exit(0); }
#!/bin/bash if [ "$3" == "nosep" ]; then #cat $2|sed "s/\x0C/\x0C\x0A/"|sed "1,/\x0C/d"|tac|sed "1,/\x0c/d"|tac|/usr/local/bin/text2pdf -o $1.pdf cat $2|sed "s/\x0C/\x0A\x0C\x0A/"|sed "1,/\x0C/d"|tac|sed "1,/\x0C/d"|tac|sed "s/\x0A\x0C/\x0C/"|/usr/local/bin/text2pdf -o $1.pdf else /usr/local/bin/text2pdf -o $1.pdf $2 fi
In der Hercules Konfiguration wird der Kartenleser als Socket Device definiert. Es ist eingestellt, dass alle Zeichen in Großbuchstaben gewandelt,abgeschnitten bzw. rechtsbündig mit Leerzeichen auf 80 Zeichen aufgefüllt und von ASCII nach EBCDIC übersetzt werden. Der Kartenleser 00A
wird an das OS durchgereicht, der Leser 00C
ist im DOS gestartet. Wegen der unterschiedlichen Behandlung in den beiden Systemen werden die Stapel zum DOS mit EOF
beendet, die zum OS mit Intervention Required
.
000A 3505 1442 sockdev ascii ucase trunc 000C 3505 2501 sockdev ascii ucase trunc eof
Das Bash Script wird als Cron Job jede Minute gestartet. Es sucht nach Dateien die vor mehr als einer Minute das letzte Mal geändert wurden und der Namenskonvention entsprechen. Wird eine solche Datei gefunden, dann wird sie mit dem Hilfsprogramm Netcat an den Socket des Kartenlesers übertragen. Dabei werden .bch Dateien an das DOS-5, .job Dateien an das SVS gesendet.
#! /bin/bash find /var/www/wiki/data/media/spool/ -name "*.bch" -amin +1 -exec bash -c "nc -v -q 0 127.0.0.1 2501 < {} && rm {}" \; find /var/www/wiki/data/media/spool/ -name "*.job" -amin +1 -exec bash -c "nc -v -q 0 127.0.0.1 1442 < {} && rm {}" \;
Das Stanzen von Karten ist eigentlich eine ideale Möglichkeit, speziell Programme und Batches aus dem DOS-5 ins Wirtssystem zu übertragen. Problematisch ist, dass die Separation der einzelnen Stapel durch Trennkarten mit Blockbuchstaben erfolgte. Erstens stellt der virtuelle Puncher im Hercules kein binäres Stanzen bereit und zweitens wäre es schwierig, diese Karten dann fruchtbringend auszuwerten.
Ein Ansatz ist, einen Drucker als Stanzer zu starten:
S cuu PUN
Wird dort JSEP=1
in der SPOOL
Anweisung angegeben, wird die normale Separatorseite mit der Information PUN FROM
ausgedruckt. Diese ließe sich wieder analog durch PRTSPOOL auswerten. Das Script /usr/local/bin/savepun
entfernt die Separatorseiten und kopiert die Datei aus Parameter 2 in das Verzeichnis, welches im Parameter 1 angegeben ist. Kurze Erläuterung: Zunächst wird an den Seitenumbruch, der am Anfang der Zeilen steht, ein Zeilenumbruch angehängt. Dann werden alle Zeilen von der ersten bis zu der, in der der erste Seitenumbruch vorkommt gelöscht. Anschliessend wird die Datei rückwärts verarbeitet (tac), wobei auch wieder die Zeilen bis zum ersten Seitenumbruch entfernt werden. Schließlich muss noch die Datei gewendet werden.
#!/bin/bash cat $2|sed "s/\x0C/\x0C\x0A/"|sed "1,/\x0C/d"|tac|sed "1,/\x0c/d"|tac>$1