Authors
Publication
Pub Details
Date
Pages
One of the major defects of QUILL is the lack of an “EXPORT” function. Only after release 2.35, PSION added the export command. But also in this case, if we want ascii text from a doc, we have to :
1) load QUILL
2) load the document
3) select the export option
4) export the document
5) quit QUILL
6) load an ascii editor
If we don’t have the 2.35 version, we can print the document to a filename, with an appropriate printer driver. In both case, there is not a simple way.
My solution is a QUILL filter. A filter, in UNIX, is a little program that applies some rules to an input file to obtain output : for example a program that translates all lowercase characters to uppercase.
For the QUILL filter, is not a simple matter, for many reasons :
1) Quill does not append a newline after each line, but only a ‘\0’ (character code zero) after each paragraph.
2) There are many control codes, i.e. character code 9 for tab, code 14 for signaling the end of document and so on.
3) The line width, indent and so on, is a mystery (for me least).
So, I arbitrarily decided on a line length of 70 chars and a page length of 60 lines , and to discard any control code except :
9 tabulation
14 end of document
12 new page
32 to 127 : ASCII code
The flow of the program is as follows:
In the first section, there is the declaration of some useful variables :
#define WIDTH 70 /** width of a line **/
#define MAXLINE 60 /** Nr of lines for page **/
#define HTAB 5 /** tab length **/
char *Paragraph;
int Page = 1; /** Start printing at page =1 **/
int Nlines = 1; /** number of lines, max 60 **/
int count;
FILE *output; These two variables are 'file pointers' :
FILE *input; with them it is possible to access a file.
Next, came the “main” function : each C program must to have a main function. The main function takes 2 parameters:
argc : an integer. The numbers of parameters passed to
the program via command line plus one, the program name
argv : a pointer to character array that holds the
parameters.
argv[0] is the program name
argv[1] is the first parameter
.. and so on …
main(register int rage, register char *argv[])
{
The following declare an integer to be ‘register’ : this value is assigned by the compiler to a 68000 register. This allows for short code and better performance, but only 8 variables may be declared as register.
register int ch; /** character analyzed **/
Next we allow a max paragraph of 32K chars :
Paragraph = (char *)malloc(32768);
/** request for a 32K buffer **/
Paragraph is an array, that can be 32768 characters long.
Now we try to open a filename, that is the first parameter : if this fails, a message is written to standard error, and then the program exits. Otherwise, the variable ‘input’ points to the filename. The first parameter must be present and it is the QUILL file to be translated.
if ( (input=fopen(argv[1],"r")) == NULL )
{
fprintf(stderr,"I cannot open the input file
!!\n");
exit(1);
}
Then, we test if the number of parameters is greater than 2 : if yes, we try to open ‘write only’ a second filename. That becomes the output of the filter. Otherwise the output is standard output.
NOTE : if you have the C68 v 4.01 compiler, the same job can be performed in the following way : “qreader QUILL.doc >filename” (this is the Unix syntax ).
output = stdout ;
if (argc>2)
{
if ( (output=fopen(argv[2],"w")) == NULL )
{
fprintf(stderr,"I cannot open the output
file !!\n");
exit(1);
}
}
The getc function reads a character from input; it stops at the end of the file. If the character is an ASCII code (characters from 32 to 127), it is stored in the Paragraph array, else it was decoded as follows : if ch is equal to 14, execute the ‘End_of_Document’ function, if ch is equal to 12, execute a ‘Newpage’ function … and so on : it is a SELect ON ch statement!
while( (ch=getc(input)) != EOF )
{
if (ch==14) End_of_Document();
if (ch==12) Newpage();
if (ch==0) Newparagraph();
if (ch==9) Htab();
if (ch>=32 && ch<=127) Paragraph[count++]=ch;
}
The statement count++ increments the variable count : it is a concise form for “count=count+1”.
The Htab function is called when a character code 9 is recognised in the input file. It evaluates the number of blanks to be added to the current line.
void Htab()
{
register int blank,j;
count % 5 => count=count MOD 5 , this is the number of blank to be added for the next tab.
blank=count % 5;
for (j=0;j<blank;j++)
Paragraph[count++]=' ';
return;
This function appends a ‘\0’ to the Paragraph array, calls ‘right_justify’ function, prints an ‘end of document’ message and exits from the program.
void End_of_Document()
{
Paragraph[count]='\0';
right_justify(Paragraph);
fprintf (output, "\n ----- End of Document -----\n");
exit(0);
}
This function appends a ‘\0’ to the Paragraph array, calls ‘right_justify’ function, prints an ‘new page’ message and returns to main after cleaned some variables : the Paragraph array, the count and Nlines. Putting a ‘\0’ at the start of Paragraph array is the C equivalent of Paragraph$=”” on SuperBASIC : that is declaring a void string, because a C string ends with ‘\0’.
void Newpage()
{
Paragraph[count]='\0';
right_justify(Paragraph);
fprintf(output,"\n ----- End of Page %2d -----\n",Page++);
Paragraph[0]='\0';
count=0;
Nlines=1;
return;
}
The following two functions are used by right_justify function, and are described below.
void Newparagraph()
{
Paragraph[count]='\0';
right_justify(Paragraph);
Paragraph[0]='\0';
count=0;
return;
}
void Check_Nlines()
{
if ( Nlines++ > MAXLINE )
{
Nlines=1;
fprintf(output,"\n ----- End of Page %2d -----\n",Page++);
}
return;
}
/**/
This is the most important function : it takes a parameter (the Paragraph array) and outputs it in lines no longer than WIDTH char, without splitting the words. If a Paragraph contains more than MAXLINE lines, the function Newpage is called : this action is performed by Check_Lines function.
void right_justify(register char *stringa)
{
register int j,k; /** 'for' variables **/
int pos = 0;
int start_pos = 0;
int len;
If Paragraph is shorter than a line, it will be printed, then the function returns to main.
len=strlen(stringa);
if (len<=WIDTH)
{
fprintf (output,"%s\n",stringa);
Check_Nlines();
return;
}
Otherwise the Paragraph array will be splitted in lines …
pos=WIDTH;
while(start_pos<len)
{
if ( pos > len )
{
for (j=start_pos;j<=len;j++)
putc(stringa[j], output);
putc('\n', output);
Check_Nlines();
return;
}
if ( stringa[pos] != ' ' )
pos--;
else {
for (j=start_pos,k=0;j<=pos;j++,k++)
putc(stringa[j], output);
putc('\n', output);
Check_Nlines();
start_pos+=k ;
pos=start_pos + k;
}
}
return;
}
For the future I hope to discover the internal structure of QUILL documents (indents, margins and so on). If this happens, I can write a postscript driver for QUILL documents.
I hope that the explanation was clear. For further details, contact me at E-mail : E.Barbaini.cons@it12.bull.it.
Full text of the program:
/******************************************************/
/********** quill reader : a simple filter ************/
/********** vs 1.08 08 Oct. 1993 ************/
/********** Emiliano Barbaini ************/
/********** E-mail E.Barbaini.cons@it12.bull.it *****/
/******************************************************/
#include <stdio.h>
#include <string.h>
void right_justify();
void End_of_Document();
void Newpage();
void Newparagraph();
void Htab();
void Check_Nlines();
#define WIDTH 70 /** width of a line **/
#define MAXLINE 60 /** Nr of lines for page **/
#define HTAB 5 /** tab length **/
#define PAR_SIZE 32768 /** max size for a paragraph **/
char *Paragraph;
int Page = 1; /** Start printing at page =1 **/
int Nlines = 1; /** number of lines, max 60 **/
int count;
FILE *output; /** output channel **/
FILE *input; /** input channel **/
main(register int argc, register char *argv[])
{
register int ch; /** character analized **/
Paragraph = (char *)malloc(32768);
/** request for a 32K buffer **/
if ( (input=fopen(argv[1],"r")) == NULL )
{
fprintf(stderr,"I cannot open the input file !!\n");
exit(1);
}
output = stdout ;
if (argc>2)
{
if ( (output=fopen(argv[2],"w")) == NULL )
{
fprintf(stderr,"I cannot open the output
file !!\n");
exit(1);
}
}
while( (ch=getc(input)) != EOF )
{
if (ch==14) End_of_Document();
if (ch==12) Newpage();
if (ch==0) Newparagraph();
if (ch==9) Htab();
if (ch>=32 && ch<=127) Paragraph[count++]=ch;
}
}
void Htab()
{
register int blank,j;
blank=count % 5;
for (j=0;j<blank;j++)
Paragraph[count++]=' ';
return;
}
void End_of_Document()
{
Paragraph[count]='\0';
right_justify(Paragraph);
fprintf (output, "\n ----- End of Document -----\n");
exit(0);
}
void Newpage()
{
Paragraph[count]='\0';
right_justify(Paragraph);
fprintf(output,"\n ----- End of Page %2d -----\n",
Page++);
Paragraph[0]='\0';
count=0;
Nlines=1;
return;
}
void Newparagraph()
{
Paragraph[count]='\0';
right_justify(Paragraph);
Paragraph[0]='\0';
count=0;
return;
}
void Check_Nlines()
{
if ( Nlines++ > MAXLINE )
{
Nlines=1;
fprintf(output,"\n ----- End of Page %2d -----\n",
Page++);
}
return;
}
/**/
void right_justify(register char *stringa)
{
register int j,k; /** 'for' variables **/
int pos = 0;
int start_pos = 0;
int len;
len=strlen(stringa);
if (len<=WIDTH)
{
fprintf (output,"%s\n",stringa);
Check_Nlines();
return;
}
pos=WIDTH;
while(start_pos<len)
{
if ( pos > len )
{
for (j=start_pos;j<=len;j++)
putc(stringa[j], output);
putc('\n', output);
Check_Nlines();
return;
}
if ( stringa[pos] != ' ' )
pos--;
else {
for (j=start_pos,k=0;j<=pos;j++,k++)
putc(stringa[j], output);
putc('\n', output);
Check_Nlines();
start_pos+=k ;
pos=start_pos + k;
}
}
return;
}
/******************************************************/
Products
Downloadable Media
Image Gallery
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.