about summary refs log tree commit diff
path: root/converter/pbm/thinkjettopbm.l
blob: c55a0085790225cbbec3291d750fea816d68ad4f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
%pointer
/*
 * A user reported in January 2005 a problem building Thinkjettopbm
 * in which the opening comment delimiter above for some reason did
 * not make it into the Lex output in the Netpbm build of this.
 * Needless to say, that would not compile.  This user was using
 * 'lex -t' on Tru64.  We did not find it worthwhile to debug it.
 *
 * Simple FLEX scanner to convert HP ThinkJet graphics image
 * to PBM format.
 *
 * Implements a small subset of ThinkJet commands.
 *
 * Copyright (C) 2001 by W. Eric Norum
 *
 * Department of Electrical Engineering
 * University of Saskatchewan
 * Saskatoon, Saskatchewan, CANADA
 * eric.norum@usask.ca
 *
 *  Permission to use, copy, modify, and distribute this software and
 *  its documentation for any purpose and without fee is hereby granted,
 *  provided that the above copyright notice appear in all copies and
 *  that both that copyright notice and this permission notice appear in
 *  supporting documentation.  This software is provided "as is" without
 *  express or implied warranty.
 *
 *  Modified 2001.04.05 by Bryan Henderson for inclusion in the Netpbm
 *  package.  Now uses Netpbm libraries and, for consistency with other
 *  Netpbm programs, does not have PGM output option.
 */

%{

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "pm_c_util.h"
#include "mallocvar.h"
#include "pbm.h"
#include "shhopt.h"

/* The following macro definitions tell Lex what sort of code to generate.
   GNU Flex does #if XXX for some of these, as opposed to #ifdef XXX, which
   means that they properly have to be set to zero instead of just left
   undefined.  (Simply leaving them undefined typically works anyway, but it
   is a problem if you use compiler options that say to fail when someone
   uses a macro he failed to define).

   We'd like to define YY_NO_UNPUT so as not to generate the unput function,
   which we don't use, and avoid a compiler warning about defining and not
   using it.  Alas, Flex 2.5.35 ignores YY_NO_UNPUT and generates the unput
   function anyway.  So we have to have a bogus reference to silence the
   unused function compiler warning.  And that means we have to generate
   the function always.  Flex 2.5.4 does respect YY_NO_UNPUT.
*/
#define YY_NO_INPUT 1
#define YY_STACK_USED 0
#define YY_ALWAYS_INTERACTIVE 0
#define YY_NEVER_INTERACTIVE 0
#define YY_MAIN 0
    /* Don't include the main() function.  We have our own */

static int yylex(void);
static int yywrap(void);
/* This works, but generates a warning 
static void yyrestart(FILE*);
*/

struct cmdlineInfo {
    /* All the information the user supplied in the command line,
       in a form easy for the program to use.
    */
    const char *inputFilespec;  /* Filespec of input file */

    unsigned int debug;
};



struct RowInfo {
    int     length;    /* length, in bytes */
    char    *bits;     /* Bitmap */
};

static int maxRowLength;
static int rowCount;
static int rowCapacity;
static struct RowInfo *rows;

static int column;

int debugFlag;
static void debug (const char *format, ...);

%}

DIG             [0-9]

%x RASTERMODE ROWMODE

%%

<ROWMODE>[\0-\377]      {
                        rows[rowCount].bits[column++] = yytext[0]; 
                        if (column >= rows[rowCount].length) {
                            rowCount++;
                            debug ("Done %d-byte row %d.\n", column, rowCount);
                            BEGIN (RASTERMODE);
                        }
                        }

<RASTERMODE>\033\*b{DIG}+W  {
                            int l;
                            if (rowCount >= rowCapacity) {
                                if (rowCapacity > INT_MAX-100)
                                    pm_error("Too many rows to count");
                                rowCapacity += 100;
                                REALLOCARRAY(rows, rowCapacity);
                                if (rows == NULL)
                                    pm_error ("Out of memory.");
                            }
                            l = atoi (yytext+3);
                            rows[rowCount].length = l;
                            rows[rowCount].bits = malloc (l);
                            if (rows[rowCount].bits == NULL)
                                pm_error ("Out of memory.");
                            if (l > maxRowLength)
                                maxRowLength = l;
                            debug ("Start %d-byte row.\n", l);
                            column = 0;
                            BEGIN (ROWMODE);
                            }

<RASTERMODE>\033\*rB   {
                       debug ("Match <esc>*rB\n");
                       BEGIN (0);
                       }

<RASTERMODE>[.\0\n]    { pm_error ("Unexpected character (%#x) in raster mode.\n", yytext[0]); }

\033\&l{DIG}+.         { debug ("Match <esc>&l\n"); }
\033\*r{DIG}+S         { debug ("Match <esc>*r#S\n"); }
\033\*b{DIG}+W         { debug ("Match <esc>*r#w\n"); }
\033\*rA               {
                       debug ("Match <esc>*rA\n");
                       BEGIN (RASTERMODE);
                       }

[\0-\377]               { /* Silently consume all other characters */ }

%%

static void
parseCommandLine(int argc, char ** const argv,
                 struct cmdlineInfo * const cmdlineP) {
/*----------------------------------------------------------------------------
   Note that the file spec array we return is stored in the storage that
   was passed to us as the argv array.
-----------------------------------------------------------------------------*/
    optEntry *option_def = malloc(100*sizeof(optEntry));
        /* Instructions to pm_OptParseOptions3 on how to parse our options.
         */
    optStruct3 opt;

    unsigned int option_def_index;

    option_def_index = 0;   /* incremented by OPTENTRY */
    OPTENT3(0,   "debug",      OPT_FLAG, NULL, &cmdlineP->debug,    0);

    opt.opt_table = option_def;
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */

    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */


    if (argc-1 < 1)
        cmdlineP->inputFilespec = "-";
    else if (argc-1 > 1)
        pm_error("Too many parameters.  There is at most one: the input "
                 "file specification.  You specified %d", argc-1);
    else
        cmdlineP->inputFilespec = argv[1];
}



/*
 * Application entry point
 */
int
main (int argc, char **argv)
{
    struct cmdlineInfo cmdline;

    pbm_init( &argc, argv );

    parseCommandLine(argc, argv, &cmdline);

    if (strlen(cmdline.inputFilespec) > 0) {
        FILE * ifP;
        ifP = freopen(cmdline.inputFilespec, "rb", stdin);
        if (ifP == NULL)
            pm_error("Unable to open file '%s' as stdin.  errno=%d (%s)", 
                     cmdline.inputFilespec, errno, strerror(errno));
    }
    debugFlag = cmdline.debug;
    yylex ();
    if (0)
        yyunput(0, NULL);  /* defeat compiler warning about unused fn */
    return 0;
}

/*
 * Finish at end of file
 */
static int 
yywrap (void)
{
    int row;
    unsigned char * packed_bitrow;

    debug ("Got %d rows, %d columns\n", rowCount, maxRowLength);

    if (maxRowLength > INT_MAX/8)
        pm_error("A row has an uncomputably large number of columns: %d",
                 maxRowLength);
    /*
     * Quite simple since ThinkJet bit arrangement matches PBM
     */
    pbm_writepbminit(stdout, maxRowLength*8, rowCount, 0);

    packed_bitrow = malloc(maxRowLength);
    if (packed_bitrow == NULL) pm_error("Out of memory");

    for (row = 0 ; row < rowCount ; row++) {
        int col;
        for (col = 0 ; col < rows[row].length ; col++) 
            packed_bitrow[col] = rows[row].bits[col];
        for (        ; col < maxRowLength;      col++)
            packed_bitrow[col] = 0;
        pbm_writepbmrow_packed(stdout, packed_bitrow, maxRowLength*8, 0);
    }
    free(packed_bitrow);
    return 1;
}

/*
 * Print debugging message
 */
static void
debug (const char *format, ...)
{
    va_list args;

    if (debugFlag) {
        fprintf (stderr, "thinkjettopbm: ");
        va_start (args, format);
        vfprintf (stderr, format, args);
        va_end (args);
    }
}