source: projects/paper-tape/userspace-driver/old implementation/cli.c @ 29

Last change on this file since 29 was 29, checked in by sven, 11 years ago

I think that was the some kind of

  • Property svn:executable set to *
File size: 14.3 KB
Line 
1/**
2 * cli.c: An exemplar, but fully functional and highly configurable
3 * command line interface (CLI) to the paper tape low level drawing
4 * routines (lochstreifen.c), which uses the famous cairo graphics
5 * library for drawing.
6 *
7 * See ./program --help for an overview about the self-explanatory
8 * arguments. By default, the program will read in any files in
9 * stdin and print the genereated PNG file on stdout.
10 *
11 * This program is written in english only (but the sourcecode
12 * contains some german comments). See an exemplar usage of this
13 * program in a PHP web program in the web-frontend subproject.
14 *
15 * This program uses the argp.h argument parser from the glibc.
16 * Thus it unfortunately won't compile with any other libc.
17 *
18 * Copyright (C) 2008  Sven Köppel
19 *
20 * This program is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU General Public License as
22 * published by the Free Software Foundation; either version 3 of
23 * the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, see
32 * <http://www.gnu.org/licenses/>.
33 *
34 **/
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <argp.h>
40
41#include "lochstreifen.h"
42
43LOCHSTREIFEN *l;
44enum _surface_type {
45        PNG_SURFACE,
46        SVG_SURFACE
47} surface_type;
48char *output_file;
49int verbosity = 0;
50int null_bytes_start = 0;
51int null_bytes_end = 0;
52
53#define DPRINTF(msg...) if(verbosity!=0) fprintf(stderr,msg);
54
55error_t parse_option (int key, char *arg, struct argp_state *state);
56cairo_pattern_t *hex2cairo_pattern(const char *string);
57
58const char *argp_program_version = "Punch card visualisator - CLI frontend";
59
60static struct argp_option options[] = {
61    {"verbose",     'v',   0,      0,  "Produce verbose output on stderr" },
62    //{"quiet",       'q',   0,      0,  "Don't produce any output" },
63    //{"silent",      's',   0,      OPTION_ALIAS },
64
65    {"output",   'o', "FILE", 0,   "Output to FILE (instead of standard output)" },
66    {"image",    'i', 0, OPTION_ALIAS },
67    {"format",   'f', "SVG|PNG",0,  "Set desired output image format (PNG or SVG)" },
68    {"", 0, 0, OPTION_DOC, ""},
69
70    {"Dimensions", 0, 0, OPTION_DOC, "Dimensions are integers without units"},
71    {"width",    'w', "NUM",      0,  "Set desired width for output image (in px or points, according to output format)", 1 },
72    {"height",   'h', "NUM",      0,  "Set desired height for output image (unit like width argument)", 1 },
73    {"diameter", 'd', "NUM",      0,  "Set dimensions for output image by punched hole diameter (pixel)", 1 },
74    {"", 0, 0, OPTION_DOC, "",1},
75
76    {"Empty bytes", 1, 0, OPTION_DOC, "Bytes with value=0x00 which are not content of files", 1},
77    {"empty-start", -1, "NUM", 0,        "Set number of empty bytes at beginning (default=0)", 2 },
78    {"empty-end",   -2, "NUM", 0,        "Set number of empty bytes at end (default=0)", 2 },
79    {"", 0, 0, OPTION_DOC, "",2},
80
81    {"Colors", 0, 0, OPTION_DOC, "Color format: #RGB[A], #RRGGBB[AA]", 2 },
82    {"hide-imagebg",    -3, 0, 0, "Make the image background (space around paper tape) transparent", 3 },
83    {"color-imagebg",   -4, "#RGBA", 0, "Set image background color", 3 },
84    {"hide-tapebg",     -5, 0, 0, "Hide the paper tape background", 3 },
85    {"color-tapebg",    -6, "#RGBA", 0, "Set tape background color", 3 },
86    {"hide-punched",    -7, 0, 0, "Hide the holes (only the punched ones)", 3 },
87    {"color-punched",   -8, "#RGBA", 0, "Set color of holes (punched bits)", 3 },
88    {"hide-notpunched", -9, 0, 0, "Hide the holes which aren't punched on real tapes", 3 },
89    {"color-notpunched",-10, "#RGBA", 0, "Set color of bits with boolean value \"false\"", 3 },
90    {"hide-feedholes",  -11, 0, 0, "Hide the feed holes (which means they wouldn't be punched)", 3 },
91    {"color-feedholes", -12, "#RGBA", 0, "Set color of feed holes (the small ones)", 3 },
92    {"", 0, 0, OPTION_DOC, "",3},
93
94    {"Transformations and Rotations", 0, 0, OPTION_DOC, "", 3},
95    {"rotation",       -13, "right|bottom|left|top", 0, "Rotation of punched tape (alternative short notation: 0=right, 1=bottom, 2=left, 3=top)", 4 },
96    {"reflection-byte-direction",  -14, 0, 0, "Enables horizontal reflecion on the data axis (inverts data direction)", 4 },
97    {"reflection-bit-direction",   -15, 0, 0, "Enables vertical reflection on the other axis (inverts bit direction)", 4 },
98    {"", 0, 0, OPTION_DOC, "",-2},
99
100    { 0 }
101};
102
103static struct argp argp = { options, parse_option, "[FILE TO READ FROM]", // Als Argument in der ersten Zeile
104    "This program uses cairo to draw a punched tape as a PNG or SVG file. Any binary " // Hilfe oben
105    "data are accepted via stdin or read in from the file given as argument. "
106    "The produced image is written into stdout or in the filename given by -o."
107    // mit \v danach koennte man ausfuehrliche Hilfe unten anzeigen.
108}; // static struct argp
109
110
111error_t parse_option (int key, char *arg, struct argp_state *state) {
112        //printf("OPTION %x (%c = %i)...\n", key, key, key);
113        switch(key) {
114                case 'v':
115                        verbosity = 1;
116                        break;
117                case 'o': case 'i':
118                        // Ausgabedatei setzen.
119                        output_file = arg;
120                        break;
121                case 'w':
122                        // set length (yeah, the legacy parameters call this width)
123                        lochstreifen_set_scaling_by_length(l, atoi(arg));
124                        break;
125                case 'h':
126                        // set width (yeah, the legacy parameters call this height)
127                        lochstreifen_set_scaling_by_width(l, atoi(arg));
128                        break;
129                case 'd':
130                        // set diameter of code holes
131                        lochstreifen_set_scaling_by_code_hole(l, atoi(arg));
132                        break;
133                case 'f':
134                        // set file format
135                        if(strcasecmp(arg, "png") == 0)
136                                surface_type = PNG_SURFACE;
137                        else if(strcasecmp(arg, "svg") == 0)
138                                surface_type = SVG_SURFACE;
139                        else
140                                argp_error(state, "Only PNG and SVG are supported as file formats.\n");
141                        break;
142                case -1:
143                        // set empty bytes at start
144                        null_bytes_start = atoi(arg);
145                        break;
146                case -2:
147                        // set empty bytes at end
148                        null_bytes_end = atoi(arg);
149                        break;
150                case -3:
151                        // hide imagebg
152                        l->outer_background_color = NULL;
153                        break;
154                case -4:
155                        // color imagebg
156                        l->outer_background_color = hex2cairo_pattern(arg);
157                        break;
158                case -5:
159                        // hide tape
160                        l->papertape_background_color = NULL;
161                        break;
162                case -6:
163                        // color tapebg
164                        l->papertape_background_color = hex2cairo_pattern(arg);
165                        break;
166                case -7:
167                        // hide punched
168                        l->one_code_hole_color = NULL;
169                        break;
170                case -8:
171                        // color punched
172                        l->one_code_hole_color = hex2cairo_pattern(arg);
173                        break;
174                case -9:
175                        // hide notpunched
176                        l->zero_code_hole_color = NULL;
177                        break;
178                case -10:
179                        // color notpunched
180                        l->zero_code_hole_color = hex2cairo_pattern(arg);
181                        break;
182                case -11:
183                        // hide feedholes
184                        l->feed_hole_color = NULL;
185                        break;
186                case -12:
187                        // color fuerhung
188                        l->feed_hole_color = hex2cairo_pattern(arg);
189                        break;
190                case -13:
191                        // rotation
192                        if     (strcasecmp(arg, "right" ) == 0) arg = "0";
193                        else if(strcasecmp(arg, "bottom") == 0) arg = "1";
194                        else if(strcasecmp(arg, "left"  ) == 0) arg = "2";
195                        else if(strcasecmp(arg, "top"   ) == 0) arg = "3";
196                        arg[1] = '\0'; // shorten string to one character
197                        //lochstreifen_set_direction(l, atoi(arg));
198                        lochstreifen_set_rotation(l, atoi(arg));
199                        break;
200                case -14:
201                        // horizontal spiegeln
202                        //lochstreifen_set_direction(l, -1, 1, -1);
203                        break;
204                case -15:
205                        // vertikal spiegeln
206                        //lochstreifen_set_direction(l, -1, -1, 1);
207                        break;
208                //case ARGP_KEY_END:
209                        //printf("bla...");
210                default:
211                        return ARGP_ERR_UNKNOWN;
212        } // switch
213        return 0; // success
214} // function parse_option
215
216
217/**
218 * Simple helper function to get a SOLID cairo pattern from a hex color string
219 * with formats like
220 *   #RGB          for example:   #FF0
221 *   #RGBA                        #A88F
222 *   #RRGGBB                      #CD438F
223 *   #RRGGBBAA                    #CD438FA0
224 *   RGB                          FF0
225 *   RGBA                         A88F
226 *   RRGGBB                       CD438F
227 *   RRGGBBAA                     CD438FA0
228 *
229 * If this method cannot parse the hex color string, it will print an error message
230 * at stderr and exit the complete program.
231 *
232 * @return a dynamically allocated cairo patern
233 **/
234cairo_pattern_t *hex2cairo_pattern(const char *string) {
235        int string_len;        // length of string without "#"
236        int x, color;          // iterators
237        long color_value[4]; // interpreted numbers
238        char *buf = "xy";       // Buffer for strtol <- one color value
239       
240        // remove a "#" char if present
241        if(string[0] == '#')
242                string++;
243        string_len = strlen(string);
244       
245        // go throught string
246        for(x=0,color=0; x < string_len && color < 5; x++,color++) {
247                // copy the current character to buffer, first position
248                buf[0] = string[x];
249                // if short notation (shorter than AABBCC), dublicate
250                // current character to buffer second position, else
251                // copy next character to second position
252                buf[1] = string_len < 6 ? string[x] : string[++x];
253                // parse buffer contents and save them as one color
254                color_value[color] = strtol(buf, NULL, 16);
255        }
256       
257        DPRINTF("Allocating '%s' as #%x%x%x%x\n", string,
258                color_value[0], color_value[1], color_value[2], (color == 4) ? color_value[3] : 0xFF);
259       
260        return cairo_pattern_create_rgba(
261                (double) color_value[0] / (double) 0xFF,
262                (double) color_value[1] / (double) 0xFF,
263                (double) color_value[2] / (double) 0xFF,
264                (color == 4) ? (double) color_value[3] / (double) 0xFF : 1
265        );
266}
267
268/**
269 * Helper function: Read contents from a stream (e.g. stdin or a file) into
270 * a byte array.
271 * Expects: stream (FILE) and a pointer to a pointer (for the target array)
272 * It will allocate an array whereby you get the pointer back.
273 * @return length of data array, counting from 1
274 *
275 * I've inspired a bit from glib's g_file_get_contents because my first
276 * version was quite buggy:
277 *****
278     * Neugeschrieben nach etwas Inspiration von der glib am 05.04.2008.
279     * Funktion get_contents_stdio, gfileutils.c im Sourcecode von glib-2.14.3.
280     * Router natürlich aus (03:11!), aber da sieht man mal wieder den Vorteil von Gentoo:
281     * Gleich alle Sourcecodes auf der Platte =)
282 *****
283 *
284 **/
285int file_get_contents(FILE *stream, byte_t **content) {
286        byte_t buf[4096];
287        size_t bytes; // gerade eben eingelesene bytes
288        byte_t *str = NULL;
289        size_t total_bytes = 0;     // alle bis jetzt eingelesenen bytes
290        size_t total_allocated = 0;
291        byte_t *tmp; // fuers realloc
292
293        while(!feof(stream)) {
294                bytes = fread(buf, 1, sizeof(buf), stream);
295
296                while( (total_bytes + bytes) > total_allocated) {
297                        if(str)
298                                total_allocated *= 2;
299                        else
300                                total_allocated = bytes > sizeof(buf) ? bytes : sizeof(buf);
301
302                        tmp = realloc(str, total_allocated);
303
304                        if(tmp == NULL) {
305                                fprintf(stderr, "*** file_get_contents ERROR*** Could not reallocate\n");
306                                //return length; // Fehler - das eingelesene zumindest zurueckgeben.
307                                // nee, gar nichts zurückgeben. Das geht so nicht.
308                                return 0;
309                        }
310
311                        str = tmp;
312                } // while innen
313
314                memcpy(str + total_bytes, buf, bytes);
315                total_bytes += bytes;
316        } // while
317
318        if(total_allocated == 0)
319                str = malloc(1); // something empty. Just to be not NULL...
320        //str[total_bytes] = '\0'; // wenns ein string wäre.
321
322        *content = str;
323        return total_bytes;
324}
325
326/**
327 * Helper function: A simple closure for a cairo_surface_t (PNG and SVG) to write out data
328 * either to a file or to stdout (given in first parameter)
329 **/
330cairo_status_t lochstreifen_out_closure(void *closure, unsigned char *data, unsigned int length) {
331        // einfach nur in das uebergebene Dateihandle schreiben
332        fwrite(data, length, 1, (FILE *)closure);
333        return CAIRO_STATUS_SUCCESS;
334}
335
336/**
337 * The main routine.
338 **/
339int main(int argc, char *argv[]) {
340        cairo_t *cr;                ///< A cairo context, given from...
341        cairo_surface_t *surface;   ///< ...this generic cairo surface
342        byte_t *data;               ///< the data array, will be filled by file_get_contents
343        int length;                 ///< the length of that data array
344        FILE *out;                  ///< an output stream handle (stdout or a file)
345        int input_argc;             ///< argp: index of argv argument where the filenames are stored
346       
347        // now starting...
348        l = lochstreifen_new();
349        argp_parse(&argp, argc, argv, 0, &input_argc, NULL);
350
351        // read input data to data array
352        if(input_argc < argc && argv[input_argc][0] != '-') {
353                // open a file (which name is not "-", because this shall read from STDIN)
354                FILE *fh;
355                DPRINTF("Reading from file %s\n", argv[input_argc]);
356                fh = fopen(argv[input_argc], "r");
357
358                if(fh == NULL) {
359                        perror("opening input file");
360                        exit(1);
361                }
362
363                length = file_get_contents(fh, &data);
364                fclose(fh);
365        } else {
366                DPRINTF("Reading from stdin, type [STRG]+[D] to generate paper tape from data\n");
367                length = file_get_contents(stdin, &data);
368        }
369
370        DPRINTF("Successfully read in %d bytes to RAM\n", length);
371        lochstreifen_set_data(l, length, data);
372        // add null bytes
373        lochstreifen_add_null_bytes(l, null_bytes_start, null_bytes_end);
374
375        // open output stream
376        if(output_file != NULL) { // check if there was an argv argument
377                out = fopen(output_file, "w");
378
379                if(out == NULL) {
380                        perror("opening output file");
381                        exit(1);
382                }
383                DPRINTF("Opened file '%s' for writing\n", output_file);
384        } else {
385                DPRINTF("Writing output data to stdout\n");
386                out = stdout;
387        }
388
389        // setting up the surface and painting...
390        if(surface_type == PNG_SURFACE) {
391                cairo_status_t status;
392                surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
393                        lochstreifen_get_target_width(l),
394                        lochstreifen_get_target_height(l)
395                );
396                cr = cairo_create(surface);
397                lochstreifen_draw(l, cr);
398
399                status = cairo_surface_write_to_png_stream(surface, (cairo_write_func_t)lochstreifen_out_closure, out);
400                DPRINTF("PNG file generated: %s\n", cairo_status_to_string(status));
401                return 0;
402        } else if(surface_type == SVG_SURFACE) {
403                surface = cairo_svg_surface_create_for_stream(
404                        (cairo_write_func_t)lochstreifen_out_closure, out,
405                        lochstreifen_get_target_width(l),
406                        lochstreifen_get_target_height(l)
407                );
408                cr = cairo_create(surface);
409                lochstreifen_draw(l, cr);
410
411                DPRINTF("SVG file generated: %s\n", cairo_status_to_string(cairo_surface_status(surface)));
412                return 0;
413        } else {
414                fprintf(stderr, "This surface is not possible, because surface_typ is an enum.\n");
415                return -42;
416        } // if surface_type
417} // main
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License