source: projects/schriften/papertapefont.c @ 16

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

yeah, it compiles with MinGW at Windows XP :)

-- Sven @ workstation at Susanne's

File size: 30.1 KB
Line 
1/**
2 * The Paper Tape Project -- Font subproject
3 * PaperTapeFont C Library / Stand alone frontend
4 *
5 * This file contains library routines (see papertapefont.h) for
6 * reading and writing "papertape font" files, as well as a
7 * powerful cross platform working stand alone program.
8 *
9 * To compile the CLI program, define STANDALONE, e.g.
10 * with
11 *
12 *   gcc papertapefont.c -DSTANDALONE=1
13 *
14 * This program uses only basis libc functions (not even getopt),
15 * so you can compile it with any M$ Windows C compiler, too.
16 * See the document "FONT_FILES.txt" in the source code directory
17 * for an introducion and manual.
18 *
19 * Initially written in a few hours (500 lines code) between
20 * 02:00 and 06:00 at 04.09.2008
21 * Improved and multiple times rewritten the next day to
22 * 1000 lines ;-)
23 *
24 * (c) 2008 Sven Köppel
25 *
26 * This program is free software; you can redistribute
27 * it and/or modify it under the terms of the GNU General
28 * Public License as published by the Free Software
29 * Foundation; either version 3 of the License, or (at
30 * your option) any later version.
31 *
32 * This program is distributed in the hope that it will
33 * be useful, but WITHOUT ANY WARRANTY; without even the
34 * implied warranty of MERCHANTABILITY or FITNESS FOR A
35 * PARTICULAR PURPOSE. See the GNU General Public License
36 * for more details.
37 *
38 * You should have received a copy of the GNU General
39 * Public License along with this program; if not, see
40 * http://www.gnu.org/licenses/.
41 *
42 **/
43
44#include "papertapefont.h"
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49byte_t papertape_font_line2byte(const char *line);
50void papertape_font_byte2line(const byte_t byte, FILE *write);
51
52/**
53 * Allocate a new PAPERTAPE_FONT object, without any character.
54 * You can use papertape_font_new_from_file() if you want to create
55 * a PAPERTAPE_FONT object from file or use papertape_font_import_from_file()
56 * or papertape_font_import_from_schematics() to fill your PAPERTAPE_FONT
57 * object.
58 **/
59// implemented as a Macro due to simplicity.
60
61/**
62 * Load a new papertape font from the file called "filename". This
63 * is some more luxurious than calling the single functions.
64 * @return allocated font object or NULL if not possible.
65 **/
66PAPERTAPE_FONT* papertape_font_new_from_file(const char* filename) {
67        FILE* file;
68        PAPERTAPE_FONT* font = papertape_font_new();
69        if(!font)
70                return NULL;
71
72        file = fopen(filename, "r");
73        if(!file) {
74                perror("Opening papertape font file");
75                return NULL;
76        }
77
78        papertape_font_load_file(font, file);
79        return font;
80}
81
82/**
83 * This will load all characters found in the filehandle (which must be readable).
84 * Characters loaded here will overwrite previous characters, if present.
85 **/
86void papertape_font_load_file(PAPERTAPE_FONT* font, FILE* fh) {
87        #define MAX_LINE   100
88        #define MAX_BYTES  30
89        #define MAX_NAME   30
90
91        // Buffer for the line string currently read in
92        char line[MAX_LINE];
93        // Buffer for the name string currently read in
94        char name[MAX_NAME] = "";
95        // Buffer for last name string read in
96        char last_name[MAX_NAME] = "";
97        // Buffer for the bytes currently read in
98        byte_t bytes[MAX_BYTES];
99        // Real number of bytes already read in
100        int bytes_n = 0;
101        // the current ascii_id
102        char ascii_id = 0;
103        // the last ascii_id
104        char last_ascii_id = 0;
105        // if we haven't already read in one byte from the new
106        // character, this is set to 1. This will force reading in the
107        // current byte.
108        int new_character = 1;
109
110        // for each character package...
111        while(fgets(line, MAX_LINE, fh) != NULL) {
112                /*
113                 * the layout of a typical   *  the layout of a typical long
114                 * ASCII line:               *  special line:
115                 *                           *
116                 *     |123.45678|x          *  |123.45678| =named_thing  comments
117                 *     012345678901          *  01234567890123456789012345678...
118                 *                           *             ||
119                 * length: 12 characters.    *    12 chars-^^- char no. 12
120                 *                           *
121                 */
122
123                /* remove "\n" lines at end. We don't need them. */
124                if(line[strlen(line)-1] == '\n')
125                        line[strlen(line)-1] = '\0';
126       
127                /* skip lines which are not a paper tape or  which have no id */
128                if(line[0] != PAPERTAPE_FONT_BORDER || strlen(line) < 12)
129                        continue;
130
131                ascii_id = line[11];
132                //fprintf(stderr, "Read in line ascii_id 0x%x (%c)\n", ascii_id, ascii_id);
133
134                // strlen: to avoid segfaults
135                if(strlen(line) > (11 + strlen(PAPERTAPE_FONT_SPECIAL_ID)) &&
136                   strncmp(line+11, PAPERTAPE_FONT_SPECIAL_ID, strlen(PAPERTAPE_FONT_SPECIAL_ID)) == 0) {
137                        // we have a special value with a full name
138                        ascii_id = 0;
139                        int z;
140                        // copy name
141                        strncpy(name, &line[11+strlen(PAPERTAPE_FONT_SPECIAL_ID)], MAX_NAME);
142                        // truncate name to only one word
143                        for(z=0; !(name[z] == ' ' || name[z] == '\0' || name[z] == '\t'); z++);
144                        name[z] = '\0';
145                }
146
147                // if we are still at the same byte like the last one...
148                if(ascii_id == last_ascii_id || //new_character ||
149                   ( ascii_id == 0 && (strcmp(name, last_name) == 0) )
150                  ) {
151                        //fprintf(stderr, "Save line for 0x%x as no. %i\n", ascii_id, bytes_n+1);
152                        // ...then add this byte to the current char byte array
153                        if(bytes_n < MAX_BYTES)
154                             // only store if enough place in bytes[] array
155                             bytes[bytes_n++] = papertape_font_line2byte(line);
156                        // first byte stored, no more new character
157                        new_character = 0;
158                } else {
159                        // we are at a new char, so let's push the last char to the list
160                        if(last_ascii_id == 0)
161                                papertape_font_set_special(font, last_name, bytes, bytes_n);
162                        else
163                                papertape_font_set_char(font, last_ascii_id, bytes, bytes_n);
164
165                        // store first byte from new character
166                        bytes[0] = papertape_font_line2byte(line);
167                        bytes_n = 1;
168                        // and tell the next loop that it has to read in.
169                        new_character = 1;
170                }
171
172                // Don't force to read in any more.
173                //if(last_ascii_id == ascii_id)
174                //      new_character = 0;
175                last_ascii_id = ascii_id;
176                strcpy(last_name, name);
177        } // while file
178       
179        // we are done, and there's still one character left to push to the list
180        if(last_ascii_id == 0)
181                papertape_font_set_special(font, last_name, bytes, bytes_n);
182        else
183                papertape_font_set_char(font, last_ascii_id, bytes, bytes_n);
184
185        // rewind the file handle (if writing is called on the same file, etc.)
186        rewind(fh);
187}
188
189/**
190 * Load Papertape font characters from binary array in order which is given
191 * by the "schematics" string.
192 *
193 **/
194void papertape_font_load_schematics(PAPERTAPE_FONT* font, const char *schematics, const byte_t *contents, int content_len) {
195        int schematics_len = strlen(schematics);
196        int schematics_x;
197        int content_offset, content_start, content_end;
198        for(schematics_x=0, content_offset=0; schematics_x < schematics_len; schematics_x++) {
199                // find start of current character
200                for(content_start = content_offset; content_start < content_len &&
201                        contents[content_start] == 0; content_start++);
202                // find end of current character
203                for(content_end = content_start; content_end < content_len &&
204                        contents[content_end] != 0; content_end++);
205                // make new character between content_start and content_end
206                papertape_font_set_char(font, schematics[schematics_x],
207                        contents + content_start, content_end - content_start);
208                // save offset for next character
209                content_offset = content_end + 1;
210        }
211}
212
213
214
215/**
216 * Parse a line to a byte. This is only used internally.
217 * Yeah, and it would be more performant to define this as a Macro, but that
218 * can be performed using gcc -O2, too.
219 **/
220byte_t papertape_font_line2byte(const char *line) {
221        byte_t value = 0;
222        #define isPunched(pos, add) if(line[pos] != ' ') { value += add; }
223        /* left border */
224        isPunched(1 ,  1);
225        isPunched(2,   2);
226        isPunched(3,   4);
227        /* feed hole */
228        isPunched(5,   8);
229        isPunched(6,  16);
230        isPunched(7,  32);
231        isPunched(8,  64);
232        isPunched(9, 128);
233        /* right border */
234        return value;
235}
236
237/**
238 * This is the invert function of papertape_font_line2byte. It writes to
239 * file handle "write" a line, but without Newline!
240 *
241 **/
242void papertape_font_byte2line(const byte_t byte, FILE *write) {
243        #define paintPunched(check) fputc((byte & check) ?\
244                PAPERTAPE_FONT_WRITE_LOGICAL_1 : ' ', write)
245        fputc(PAPERTAPE_FONT_BORDER, write); /* left border */
246        paintPunched(  1); /* bit 1 */
247        paintPunched(  2); /* bit 2 */
248        paintPunched(  4); /* bit 3 */
249        fputc(PAPERTAPE_FONT_FEED_HOLE, write); /* feed hole */
250        paintPunched(  8); /* bit 4 */
251        paintPunched( 16); /* bit 5 */
252        paintPunched( 32); /* bit 6 */
253        paintPunched( 64); /* bit 7 */
254        paintPunched(128); /* bit 8 */
255        fputc(PAPERTAPE_FONT_BORDER, write); /* right border */
256}
257
258
259/**
260 * This function adds or changes special characters in the papertape font.
261 * These characters are typically white spaces or word spacings.
262 *
263 * @param font The Papertape Font object, e.g. made by papertape_font_new_...()
264 * @param name The Name ID
265 * @param bytes Your byte array which is supposed to represent the character. The function copies it.
266 * @param bytes_n The length of your byte array (counting from 1, of course)
267 * @return 0 if successfully added, 1 otherwise. (It will never return 1 ;-) )
268 **/
269PAPERTAPE_FONT_CHAR* papertape_font_set_special(PAPERTAPE_FONT* font, const char *name, const byte_t* bytes, int bytes_n) {
270        // allocate the new character structure
271        PAPERTAPE_FONT_CHAR *buf;
272        buf = (PAPERTAPE_FONT_CHAR*) malloc(sizeof(PAPERTAPE_FONT_CHAR));
273
274        // copy all values to structure
275        buf->ascii_id = 0x0;
276        buf->bytes_n = bytes_n;
277        buf->bytes = (byte_t*) malloc(bytes_n);
278        memcpy(buf->bytes, bytes, bytes_n);
279        buf->name = (char*) malloc(strlen(name));
280        strcpy(buf->name, name);
281       
282        // Pack it at the very start
283        buf->next = (font->next != NULL) ? font->next : NULL;
284        font->next = buf;
285
286        return buf;
287}
288
289/**
290 * This is the main function how to add or change a character in the papertape_font.
291 * Internally, the papertape_font works with a ascii ordered single linked list. So
292 * there's no other write access to that list than this function.
293 *
294 * If you want to set/add characters with special long names, use papertape_font_set_special_char.
295 *
296 * @param font The Papertape Font object, e.g. made by papertape_font_new_...()
297 * @param ascii_id The ASCII value of the character you want to add.
298 * @param bytes Your byte array which is supposed to represent the character. The function copies it.
299 * @param bytes_n The length of your byte array (counting from 1, of course)
300 * @return A link to the generated character. You don't need it, anyway.
301 **/
302PAPERTAPE_FONT_CHAR* papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n) {
303        PAPERTAPE_FONT_CHAR *current;
304        PAPERTAPE_FONT_CHAR *next;
305       
306        // allocate the new character structure
307        PAPERTAPE_FONT_CHAR *buf;
308        buf = (PAPERTAPE_FONT_CHAR*) malloc(sizeof(PAPERTAPE_FONT_CHAR));
309
310        // copy all values to structure
311        buf->ascii_id = ascii_id;
312        buf->bytes_n = bytes_n;
313        buf->bytes = (byte_t*) malloc(bytes_n);
314        memcpy(buf->bytes, bytes, bytes_n);
315        buf->name = NULL;
316        buf->next = NULL;
317
318        // Start going throught the list
319        current = font; // Start with the font object as first list member.
320        while(current->next != NULL) {
321                if(current->next->ascii_id == ascii_id) {
322                        // the next one has the same ascii_id as we have (perhaps
323                        // we are 'c' and the next is 'c', too). So replace the next
324                        // one.
325                        next = current->next;  // copy the pointer somewhere else
326                        current->next = buf;   // replace the next pointer with our char
327                        free(next);            // delete the old next pointer.
328                        return buf;
329                } else if(current->next->ascii_id > ascii_id) {
330                        // the next one has a bigger ascii_id as we have (perhapse
331                        // we are 'c' while the next is 'e'). So pack our character
332                        // right here, between current and next.
333                        next = current->next;  // copy the pointer somewhere else
334                        current->next = buf;   // replace the next pointer with our char
335                        buf->next = next;      // pack the next pointer after our pointer
336                        return buf;
337                } else {
338                        // the next one has a lower ascii_id as we have, so we have
339                        // to go on throught the list
340                        current = current->next;
341                }
342        }
343
344        // we have reached the end of the list and haven't found any ascii_id biggger
345        // or equal to ours. So we simply add our character to the list.
346        current->next = buf;
347        return buf;
348}
349
350/**
351 * This is an auxillary function to access the linked list. It's not supposed to be used
352 * from outside -- use highlevel functions like set_char or get_label.
353 * @returns Link inside the list, NULL if not found.
354 **/
355PAPERTAPE_FONT_CHAR* papertape_font_low_get_char(PAPERTAPE_FONT* font, char ascii_id) {
356        PAPERTAPE_FONT_CHAR* current;
357        current = font;
358        while(current->next != NULL) {
359                if(current->next->ascii_id == ascii_id) {
360                        // we have found the right character entry.
361                        return current->next;
362                } else
363                        current = current->next;
364        }
365        return NULL;
366}
367
368/**
369 * This is just like papertape_font_low_get_char, but for special characters.
370 * Ah, one special thing: Special characters are always valid, even if not defined.
371 **/
372PAPERTAPE_FONT_CHAR* papertape_font_low_get_special(PAPERTAPE_FONT* font, const char* name) {
373        PAPERTAPE_FONT_CHAR* current;
374        byte_t default_bytes[] = { 0 };
375        current = font;
376        while(current->next != NULL) {
377                if(current->next->name != NULL &&
378                   strcmp(current->next->name, name) == 0) {
379                        // we have found the right character entry.
380                        return current->next;
381                } else
382                        current = current->next;
383        }
384        // no special character found. We'll generate one.
385        return papertape_font_set_special(font, name, default_bytes, 1);
386}
387
388
389/**
390 * I don't know why one wanted to delete a character, but by using this function you can
391 * do exactly that.
392 * @returns 1 if successful deleted, 0 if not found in list.
393 **/
394int papertape_font_del_char(PAPERTAPE_FONT* font, char ascii_id) {
395        // we cannot use papertape_font_low_get_char, because this would need
396        // a double linked list.
397        PAPERTAPE_FONT_CHAR* current;
398        current = font;
399        while(current->next != NULL) {
400                if(current->next->ascii_id == ascii_id) {
401                        // we have found the right character entry.
402                        if(current->next->next != NULL) {
403                                // we are somewhere in the list, so we have to rewrite
404                                // the "next" pointer.
405                                current->next = current->next->next;
406                        }
407                        free(current->next);
408                        return 1;
409                } else
410                        current = current->next;
411        }
412        return 0;
413}
414
415/**
416 * This is the same like for normal characters, only for special characters.
417 **/
418int papertape_font_del_special(PAPERTAPE_FONT* font, const char* name) {
419        PAPERTAPE_FONT_CHAR* current;
420        current = font;
421        while(current->next != NULL) {
422                if(current->next->name != NULL &&
423                   strcmp(current->next->name, name) == 0) {
424                        // we have found the right character entry.
425                        if(current->next->next != NULL) {
426                                // we are somewhere in the list, so we have to rewrite
427                                // the "next" pointer.
428                                current->next = current->next->next;
429                        }
430                        free(current->next);
431                        return 1;
432                } else
433                        current = current->next;
434        }
435        return 0;
436}
437
438/**
439 * This is the main function from the papertape_font package. Using this function, you
440 * can render any ASCII string to a papertape label, using the current font.
441 *
442 * The return byte array will be malloced, and you'll have after calling the only
443 * link to that array. So feel free to free() it, if you don't need it any more.
444 *
445 * If there's a non printable character in your string, this function will print out an
446 * error message at stderr and skip that character. Use papertape_font_string_is_printable
447 * if you want to check wheter your string contains non printable characters.
448 *
449 * Internally speaken, this is the only read access function to the internal single
450 * linked font character list.
451 *
452 **/
453byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string, int* size) {
454        PAPERTAPE_FONT_CHAR* character_spacing = papertape_font_low_get_special(font, PAPERTAPE_FONT_CHARACTER_SPACING_NAME);
455        int x;       // to count throught the string
456        *size = 0;   // the size of the output byte array
457        byte_t* output;
458        int output_pos;  // current position in output array
459        int clen = strlen(string)*2 - 1; // length of characters stack array
460        // dynamic stack arrays are ISO C99, compare http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Variable-Length.html
461        PAPERTAPE_FONT_CHAR *characters[ clen ];
462        int cpos = 0; // current position in character array
463       
464        // pack the font char array together
465        for(x=0; string[x] != '\0'; x++) {
466                // find white spaces
467                if(string[x] == ' ')
468                        characters[cpos] = papertape_font_low_get_special(font, PAPERTAPE_FONT_WHITE_SPACE_NAME);
469                else
470                        characters[cpos] = papertape_font_low_get_char(font, string[x]);
471
472                if(characters[cpos] == NULL) {
473                        fprintf(stderr, "papertape_font_get_label: Character \"%c\" at position %i in \"%s\" not printable\n",
474                                string[x], x+1, string);
475                        // tell the malloc loop (see below) to loop not over the complete
476                        // characters[] array, because one character is missing. That is,
477                        // pop the array for 1 value.
478                        clen--;
479                        // don't increase cpos, we want to overwrite this character in the
480                        // next loop.
481                        continue;
482                } else if(characters[cpos]->bytes == NULL) {
483                        // for all possibilities... this character is empty.
484                        // Make sure bytes_n is it, too.
485                        if(characters[cpos]->bytes_n != 0)
486                                fprintf(stderr, "papertape_font_get_label: Bad font parsed! Character 0x%x (%c) not really empty!\n",
487                                        characters[cpos]->ascii_id, characters[cpos]->ascii_id);
488                }
489
490                *size += characters[cpos]->bytes_n;
491                cpos++;
492               
493                // add the characters spacing after each character
494                if(x+1 < strlen(string)) { // but not at the last character
495                        characters[cpos] = character_spacing;
496                        *size += characters[cpos]->bytes_n;
497                        cpos++;
498                }
499        }
500       
501        // now malloc the output byte array and generate it
502        output = (byte_t*) malloc(*size);
503        for(cpos=0,output_pos=0; cpos < clen && output_pos < *size; output_pos += characters[cpos++]->bytes_n)
504                memcpy(output+output_pos, characters[cpos]->bytes, characters[cpos]->bytes_n);
505
506        // we are done.
507        return output;
508}
509
510/**
511 * Checks if there's any non printable character in your string. Papertape fonts don't have
512 * to have bit masks for every character, so use this function to check your string.
513 * papertape_font_get_label will output errors to stderr if it finds unprintable
514 * characters.
515 *
516 * @returns 1 if all characters are printable, 0 otherwise.
517 **/
518int papertape_font_string_is_printable(PAPERTAPE_FONT *font, const char* string) {
519        int x;
520
521        for(x=0; string[x] != '\0'; x++) {
522                if(string[x] == ' ')
523                        // white spaces are always printable
524                        continue;
525                if(papertape_font_low_get_char(font, string[x]) == NULL)
526                        // character not found!
527                        return 0;
528        }
529
530        return 1; // No non printable char found.
531}
532
533/**
534 * Write the current PAPERTAPE_FONT (back) out to a file.
535 * FILE mus be a writeable handle.
536 **/
537int papertape_font_write_to_file(PAPERTAPE_FONT* font, FILE *fh) {
538        PAPERTAPE_FONT_CHAR* current;
539        int x;
540
541        // Write first lines.
542        fputs(PAPERTAPE_FONT_WRITE_FIRST_LINES, fh);
543       
544        // now go throught list and write all characters out.
545        current = font;
546        while(1) {
547                // write bytes and identifiers
548                for(x=0; x<current->bytes_n; x++) {
549                        papertape_font_byte2line(current->bytes[x], fh);
550                        if(current->ascii_id == 0) {
551                                fputs(PAPERTAPE_FONT_SPECIAL_ID, fh);
552                                fputs(current->name, fh);
553                        } else
554                                fputc(current->ascii_id, fh);
555                        fputc('\n', fh);
556                }
557       
558                // and go on. Or break, if at end.
559                if(current->next != NULL) {
560                        // write a neat NULL byte as eye candy seperation
561                        papertape_font_byte2line(0x0, fh);
562                        fputc('\n', fh);
563                        current = current->next;
564                } else
565                        break;
566        }
567       
568        return 1; // could be void.
569}
570/**
571 * This will print a dump of the papertape font linked list, just for debugging purpose,
572 * to the file handle. It's just fine to give "stderr" at the file handle.
573 **/
574void papertape_font_dump(PAPERTAPE_FONT *font, FILE* fh) {
575        PAPERTAPE_FONT_CHAR* current;
576        int ram_bytes = 0;
577        int i = 0;
578        int x;
579
580        current = font;
581        while(1) {
582                ram_bytes += sizeof(PAPERTAPE_FONT_CHAR);
583                if(current->ascii_id == 0 && current->name == NULL)
584                        fprintf(fh, "%i. char: Special character, no name (broken), %i bytes allocated\n", i++, current->bytes_n);
585                else if(current->ascii_id == 0) {
586                        fprintf(fh, "%i. char: Special character, name \"%s\", %i bytes allocated\n", i++, current->name, current->bytes_n);
587                        ram_bytes += strlen(current->name)+1;
588                } else
589                        fprintf(fh, "%i. char: \"%c\" (0x%x), %i bytes allocated\n", i++, current->ascii_id, current->ascii_id, current->bytes_n);
590                if(current->bytes == NULL) {
591                        fprintf(fh, "No bytes allocated!\n");
592                } else {
593                        ram_bytes += current->bytes_n;
594                        for(x=0; x < current->bytes_n; x++) {
595                                papertape_font_byte2line(current->bytes[x], fh);
596                                fputc('\n', fh);
597                        }
598                }
599               
600                if(current->next == NULL) {
601                        fprintf(fh, "End of list.\n");
602                        fprintf(fh, "Statistics: %i entries, %i total bytes in heap\n", i, ram_bytes);
603                        return;
604                } else
605                        current = current->next;
606        }
607}
608
609/**
610 * The main() method (and auxillary helper functions) is only compiled when the switch
611 * "STANDALONE" is defined. If not, we'll only get the simple library.
612 *
613 **/
614#ifdef STANDALONE
615
616/**
617 * A helper method for the standalone main(): Read contents of a stream
618 * in a dynamically allocated heap byte array.
619 **/
620int file_get_contents(FILE *stream, byte_t **content) {
621        byte_t buf[4096];
622        size_t bytes; // gerade eben eingelesene bytes
623        byte_t *str = NULL;
624        size_t total_bytes = 0;     // alle bis jetzt eingelesenen bytes
625        size_t total_allocated = 0;
626        byte_t *tmp; // fuers realloc
627
628        while(!feof(stream)) {
629                bytes = fread(buf, 1, sizeof(buf), stream);
630
631                while( (total_bytes + bytes) > total_allocated) {
632                        if(str)
633                                total_allocated *= 2;
634                        else
635                                total_allocated = bytes > sizeof(buf) ? bytes : sizeof(buf);
636
637                        tmp = realloc(str, total_allocated);
638
639                        if(tmp == NULL) {
640                                fprintf(stderr, "*** file_get_contents ERROR*** Could not reallocate\n");
641                                //return length; // Fehler - das eingelesene zumindest zurueckgeben.
642                                // nee, gar nichts zurückgeben. Das geht so nicht.
643                                return 0;
644                        }
645
646                        str = tmp;
647                } // while innen
648
649                memcpy(str + total_bytes, buf, bytes);
650                total_bytes += bytes;
651        } // while
652
653        if(total_allocated == 0)
654                str = (byte_t*) malloc(1); // something empty. Just to be not NULL...
655        //str[total_bytes] = '\0'; // wenns ein string wäre.
656
657        *content = str;
658        return total_bytes;
659}
660
661/**
662 * The main method. We are using an own parameter system with extensive help messages.
663 * No so much parameter flexibility, but it should be enough :-)
664 **/
665int main(int argc, char **argv) {
666        // Instead of poor getopt emulation, an own parameter system like "svn" CLI client has it
667        if(argc < 3 || strcasecmp(argv[1], "help") == 0) {
668                // argc == 1: Wrong call like "./font", missing subcommand.
669                // Wrong call (like "./font label"), or help call (like "./font help").
670                // Display help. Or startup help system.
671                if(argc < 3) {
672                        fprintf(stderr, "Usage: %s <subcommand> [subcommand option] <font file>\n"
673                                "The Papertape font utility program.\n"
674                                "Type '%s help <subcommand>' for help on a specific subcommand.\n"
675                                "The <subcommand> and <font file> parameters are mandatory\n"
676                                "\n"
677                                "Available subcommands:\n"
678                                "   help        Starts up this help system\n"
679                                "   label       Prints out a complete label, given as the option\n"
680                                "   set         Sets the character with the ASCII ID (e.g. 0x20), reads from STDIN\n"
681                                "   del         Deletes the character with the ASCII ID from the font file\n"
682                                "   dump        Parses the font file and prints out the result, for debugging\n"
683                                "   export      Exports the font file to STDOUT, according to schematics\n"
684                                "   import      Imports the font file, according to schematics\n"
685                                "               Default schematics: %s\n",
686                                argv[0], argv[0], PAPERTAPE_FONT_SCHEMATICS_DEFAULT);
687                }
688                               
689                if(argc >= 3) {
690                        // verbose Help system.
691                        if(strcasecmp(argv[2], "label") == 0) {
692                                fprintf(stderr, "label: Print out the compiled bytes for the translated label.\n"
693                                                "Expects String as subcommand option.\n\n"
694                                                "Example calls:\n"
695                                                " Prints out yor text to STDOUT.\n"
696                                                "   $ %s label \"The text you want to label\" your-font-file.ptf:\n"
697                                                " The same with \"hello world\". Upper/lowercase produces the same result, btw.\n"
698                                                "   $ %s label hello\\ world your-font-file.ptf:\n"
699                                                " This writes the output to the file hallo-welt.bin:\n"
700                                                "   $ %s label hallo\\ Welt your-font-file.ptf > hallo-welt.bin\n"
701                                                " This will punch out all files in the directory with the right label before\n"
702                                                "   $ for x in *; do %s label \"$x\" ../fonts/your.ptf | cat - \"$x\" | ./punch; done;\n",
703                                                argv[0], argv[0], argv[0], argv[0]);
704                        } else if(strcasecmp(argv[2], "set") == 0) {
705                                fprintf(stderr, "set: Changes or adds a character in the font file\n"
706                                                "Expects the wanted character as hex or character as subcommand option.\n\n"
707                                                "Example calls:\n"
708                                                " This will create a new font file that contains the character 'a':\n"
709                                                "   $ cat how-a-should-look-like.bin | %s set a new-font-file.ptf\n"
710                                                " This will overwrite the current value of the character 'a':\n"
711                                                "   $ cat a-new-look-for-letter-a.bin | %s set a new-font-file.ptf\n"
712                                                " This will set the blank space character:\n"
713                                                "   $ echo -en \"\\0\" | %s set \" \" existing-font-file.ptf\n"
714                                                " This will do exactly the same like above:\n"
715                                                "   $ echo -en \"\\0\" | %s set 0x20 existing-font-file.ptf\n",
716                                                argv[0], argv[0], argv[0], argv[0]);
717                        } else if(strcasecmp(argv[2], "del") == 0) {
718                                fprintf(stderr, "del: Deletes one or more characters from a font file\n"
719                                                "Expects one character as subcommand option.\n\n"
720                                                "Example calls:\n"
721                                                " Delete a 'm' from your font file:\n"
722                                                "   $ %s del m your-font-file.ptf\n",
723                                                argv[0]);
724                        } else if(strcasecmp(argv[2], "dump") == 0) {
725                                fprintf(stderr, "dump: Parse a font file and print out the results to stdout. This is\n"
726                                                "pracitcal for detecting syntax errors in the font file.\n\n"
727                                                "Example calls:\n"
728                                                "  Get a dump:\n"
729                                                "   $ %s dump your-font-file.ptf\n"
730                                                "  Save it somewhere:\n"
731                                                "   $ %s dump your-font-file.ptf > dump.txt\n",
732                                                argv[0], argv[0]);
733                        } else if(strcasecmp(argv[2], "export") == 0|| strcasecmp(argv[2], "import") == 0) {
734                                fprintf(stderr, "import/export: Converting font files to complete binary files on the fly\n"
735                                                "You can give a scheme optionally as subcommand option.\n"
736                                                "This is the default scheme:\n"
737                                                "   %s\n\n"
738                                                "Example calls:\n"
739                                                " Export an existing font file, edit the file with an hex editor and import it again:\n"
740                                                "   $ %s export your-font-file.ptf > font-file-export.bin\n"
741                                                "   $ khexdump font-file-export.bin\n"
742                                                "          [ some GUI editing, and so on...]\n"
743                                                "   $ cat font-file-export.bin | %s import your-font-file.ptf\n"
744                                                " Import to a new font file from existin g other font mechanism:\n"
745                                                "   $ SCHEME=\"abcdefgh...4567890 ()<>\"\n"
746                                                "   $ perl another-font.pl \"$SCHEME\" | %s import \"%s\" importet-font-file.ptf\n"
747                                                " Make a new font file which only contains the digits of the old file:\n"
748                                                "   $ %s export \"0123456789\" existing.ptf | %s import \"0123456789\" new.ptf\n",
749                                                PAPERTAPE_FONT_SCHEMATICS_DEFAULT, argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]);
750                        } else {
751                                fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n",
752                                        argv[2], argv[0]);
753                        }
754                } // verbose help
755                return 0;
756        } // Help messages
757
758        if(strcasecmp(argv[1], "export") == 0 || strcasecmp(argv[1], "label") == 0) {
759                // export or label.
760                int size;
761                byte_t *bytes;
762                PAPERTAPE_FONT *font;
763                 
764                if(! (font=papertape_font_new_from_file(argc == 4 ? argv[3] : argv[2]) )) {
765                        fprintf(stderr, "Error creating Papertape font.\n");
766                        return 3;
767                }
768
769                bytes = papertape_font_get_label(font, argc == 4 ? argv[2] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT, &size);
770                fwrite(bytes, size, sizeof(byte_t), stdout);   
771                return 0;
772        } else if(strcasecmp(argv[1], "set") == 0 || strcasecmp(argv[1], "del") == 0) {
773                // set and delete
774                char character;
775                FILE *font_file;
776                PAPERTAPE_FONT *font;
777               
778                // at first: get the character
779                if(argc == 3) {
780                        fprintf(stderr, "%s: Too few arguments! Missing character. Type '%s help %s' for usage.\n",
781                                argv[1], argv[0], argv[1]);
782                        return 1;
783                }
784                if(strlen(argv[2]) > 1)
785                        // A Number like "255", 0x20 or 080. strol will parse it.
786                        character = (char)strtol(argv[2], NULL, 0);
787                else
788                        // only one character in ASCII.
789                        character = argv[2][0];
790
791                // open file for reading and WRITING.
792                font_file = fopen(argv[3], "r+");
793                if(!font_file) {
794                        perror("Opening font file for writing");
795                        return 2;
796                }
797                font = papertape_font_new();
798                papertape_font_load_file(font, font_file);
799
800                if(strcasecmp(argv[1], "set")) {
801                        // change/add character
802                        byte_t *contents;
803                        int bytes = file_get_contents(stdin, &contents);
804                        printf("Read in %i bytes for letter %c (seen as byte: 0x%x)\n", bytes, character, character);
805                        papertape_font_set_char(font, character, contents, bytes);
806                        printf("Set character %c successfully.\n", character);
807                } else {
808                        // delete character
809                        printf(papertape_font_del_char(font, character) ? 
810                                "Deleted %c successfully from file %s\n" :
811                                "Could not find %c in file %s!\n",
812                                character, argv[3]);
813                }
814               
815                // write papertape file.
816                papertape_font_write_to_file(font, font_file);
817                return 0;
818        } else if(strcasecmp(argv[1], "dump") == 0) {
819                PAPERTAPE_FONT* font = papertape_font_new_from_file(argv[2]);
820                printf("This is a dump from the internal single linked ordered list,\n"
821                        "created from the file '%s':\n\n", argv[2]);
822                papertape_font_dump(font, stdout);
823                return 0;
824        } else if(strcasecmp(argv[1], "import") == 0) {
825                // run an import.
826                PAPERTAPE_FONT *font = papertape_font_new();
827                FILE* font_file;
828                byte_t *contents;
829                int content_len;
830                char *schematics;
831               
832                // open file for DESTROYING WRITING.
833                font_file = fopen(argc == 4 ? argv[3] : argv[2], "w");
834                if(font_file == NULL) {
835                        perror("Opening font file for writing");
836                        return 2;
837                }
838               
839                schematics = argc == 4 ? argv[2] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT;
840
841                fprintf(stderr, "Reading from stdin, expecting schematics %s\n", schematics);
842                content_len = file_get_contents(stdin, &contents);
843                // debug: print it out!
844                // fwrite(contents, 1, content_len, stdout); exit(0);
845       
846                // Parse schematics
847                papertape_font_load_schematics(font, schematics, contents, content_len);
848
849                fprintf(stderr, "Readed from stdin successfully. Here you get a dump:\n");
850                papertape_font_dump(font, stdout);
851
852                // that's it, we are done. Write the papertape font.
853                papertape_font_write_to_file(font, font_file);
854                return 0;
855        } else {
856                fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n",
857                        argv[2], argv[0]);
858                return 1;
859        }
860} // main
861#endif /* STANDALONE */
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