Changeset 13 in projects


Ignore:
Timestamp:
Sep 5, 2008, 1:58:47 AM (11 years ago)
Author:
sven
Message:

Greatly improved the papertapefont algorithms and routines. Now even tested.
Everything compiles with "-Wall -O2" and is, of course, very performant :-)

Added detailed documentation in FONT_FILES.txt.

-- Sven @ dual head workstation

Location:
schriften
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • schriften/papertapefont.c

    r12 r13  
    55 * This file contains library routines (see papertapefont.h) for
    66 * reading and writing "papertape font" files, as well as a
    7  * stand alone program.
     7 * powerful cross platform working stand alone program.
    88 *
    99 * To compile the CLI program, define STANDALONE, e.g.
     
    1212 *   gcc papertapefont.c -DSTANDALONE=1
    1313 *
     14 * See the document "FONT_FILES.txt" in the source code directory
     15 * for an introducion and manual.
     16 *
    1417 * Initially written in a few hours (500 lines code) between
    1518 * 02:00 and 06:00 at 04.09.2008
     19 * Improved and multiple times rewritten the next day to
     20 * 1000 lines ;-)
    1621 *
    1722 * (c) 2008 Sven Köppel
     
    4045#include <string.h>
    4146
    42 #define PAPERTAPE_FONT_MAX_LINE_LENGTH  100
    43 
    4447byte_t papertape_font_line2byte(const char *line);
    45 
    46 PAPERTAPE_FONT* papertape_font_new_from_file(FILE* fh) {
    47         PAPERTAPE_FONT* font;
    48         char line[PAPERTAPE_FONT_MAX_LINE_LENGTH];
    49         char ascii_id;
    50         byte_t* bytes;
    51         int bytes_n;
    52         int x;
    53 
    54         // allocate new font object
    55         font = malloc(sizeof(PAPERTAPE_FONT));
    56         if(!font || !fh)
     48void papertape_font_byte2line(const byte_t byte, FILE *write);
     49
     50/**
     51 * Allocate a new PAPERTAPE_FONT object, without any character.
     52 * You can use papertape_font_new_from_file() if you want to create
     53 * a PAPERTAPE_FONT object from file or use papertape_font_import_from_file()
     54 * or papertape_font_import_from_schematics() to fill your PAPERTAPE_FONT
     55 * object.
     56 **/
     57// implemented as a Macro due to simplicity.
     58
     59/**
     60 * Load a new papertape font from the file called "filename". This
     61 * is some more luxurious than calling the single functions.
     62 * @return allocated font object or NULL if not possible.
     63 **/
     64PAPERTAPE_FONT* papertape_font_new_from_file(const char* filename) {
     65        FILE* file;
     66        PAPERTAPE_FONT* font = papertape_font_new();
     67        if(!font)
    5768                return NULL;
    5869
    59         // set up as ensurance, against segfaults or simple fonts:
    60         font->ascii_id = PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID;
    61         font->bytes = PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES;
    62         font->bytes_n = sizeof(PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES);
     70        file = fopen(filename, "r");
     71        if(!file) {
     72                perror("Opening papertape font file");
     73                return NULL;
     74        }
     75
     76        papertape_font_load_file(font, file);
     77        return font;
     78}
     79
     80/**
     81 * This will load all characters found in the filehandle (which must be readable).
     82 * Characters loaded here will overwrite previous characters, if present.
     83 **/
     84void papertape_font_load_file(PAPERTAPE_FONT* font, FILE* fh) {
     85        #define MAX_LINE   100
     86        #define MAX_BYTES  30
     87        #define MAX_NAME   30
     88
     89        // Buffer for the line string currently read in
     90        char line[MAX_LINE];
     91        // Buffer for the name string currently read in
     92        char name[MAX_NAME] = "";
     93        // Buffer for last name string read in
     94        char last_name[MAX_NAME] = "";
     95        // Buffer for the bytes currently read in
     96        byte_t bytes[MAX_BYTES];
     97        // Real number of bytes already read in
     98        int bytes_n = 0;
     99        // the current ascii_id
     100        char ascii_id = 0;
     101        // the last ascii_id
     102        char last_ascii_id = 0;
     103        // if we haven't already read in one byte from the new
     104        // character, this is set to 1. This will force reading in the
     105        // current byte.
     106        int new_character = 1;
     107
     108        // for each character package...
     109        while(fgets(line, MAX_LINE, fh) != NULL) {
     110                /*
     111                 * the layout of a typical   *  the layout of a typical long
     112                 * ASCII line:               *  special line:
     113                 *                           *
     114                 *     |123.45678|x          *  |123.45678| =named_thing  comments
     115                 *     012345678901          *  01234567890123456789012345678...
     116                 *                           *             ||
     117                 * length: 12 characters.    *    12 chars-^^- char no. 12
     118                 *                           *
     119                 */
     120
     121                /* remove "\n" lines at end. We don't need them. */
     122                if(line[strlen(line)-1] == '\n')
     123                        line[strlen(line)-1] = '\0';
    63124       
    64         /*{
    65                 byte_t[] def_spacing = PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES;
    66                 papertape_font_set_char(font, PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID,
    67                         def_spacing, sizeof(def_spacing));
    68                
    69         }*/
    70 
    71         // now parse file.
    72 
    73         // for each character package...
    74         while(fgets(line, PAPERTAPE_FONT_MAX_LINE_LENGTH, fh)) {
    75                 // skip comment lines. They are only allowed at
    76                 // index positions, not in the data byte lines.
    77                 if(line[0] == '#')
     125                /* skip lines which are not a paper tape or  which have no id */
     126                if(line[0] != PAPERTAPE_FONT_BORDER || strlen(line) < 12)
    78127                        continue;
    79128
    80                 ascii_id = line[0];
    81                 bytes_n = atoi(&line[1]); // atoi will remove spaces
    82                 bytes = calloc(sizeof(byte_t), bytes_n); // allocate place for bytes
    83 
    84                 // parse the bytes notation
    85                 for(x=0; x < bytes_n; x++) {
    86                         // break at end of line
    87                         if(!fgets(line, PAPERTAPE_FONT_MAX_LINE_LENGTH, fh))
    88                                 break;
    89                         // add parsed line to byte array
    90                         bytes[x] = papertape_font_line2byte(line);
    91                 }
    92 
    93                 papertape_font_set_char(font, ascii_id, bytes, bytes_n);
    94                 // papertape_font_set_char copies the byte, so we free them here
    95                 free(bytes);
    96         }
    97 }
     129                ascii_id = line[11];
     130                //fprintf(stderr, "Read in line ascii_id 0x%x (%c)\n", ascii_id, ascii_id);
     131
     132                // strlen: to avoid segfaults
     133                if(strlen(line) > (11 + strlen(PAPERTAPE_FONT_SPECIAL_ID)) &&
     134                   strncmp(line+11, PAPERTAPE_FONT_SPECIAL_ID, strlen(PAPERTAPE_FONT_SPECIAL_ID)) == 0) {
     135                        // we have a special value with a full name
     136                        ascii_id = 0;
     137                        int z;
     138                        // copy name
     139                        strncpy(name, &line[11+strlen(PAPERTAPE_FONT_SPECIAL_ID)], MAX_NAME);
     140                        // truncate name to only one word
     141                        for(z=0; !(name[z] == ' ' || name[z] == '\0' || name[z] == '\t'); z++);
     142                        name[z] = '\0';
     143                }
     144
     145                // if we are still at the same byte like the last one...
     146                if(new_character || ascii_id == last_ascii_id ||
     147                   ( ascii_id == 0 && (strcmp(name, last_name) == 0) )
     148                   ) {
     149                        //fprintf(stderr, "Save line for 0x%x as no. %i\n", ascii_id, bytes_n+1);
     150                        // ...then add this byte to the current char byte array
     151                        if(bytes_n < MAX_BYTES)
     152                             // only store if enough place in bytes[] array
     153                             bytes[bytes_n++] = papertape_font_line2byte(line);
     154                        // leave "last_" values as they are, because they haven't changed anyway
     155                } else {
     156                        // we are at a new char, so let's push the last char to the list
     157                        if(last_ascii_id == 0)
     158                                papertape_font_set_special(font, last_name, bytes, bytes_n);
     159                        else
     160                                papertape_font_set_char(font, last_ascii_id, bytes, bytes_n);
     161
     162                        // store first byte from new character
     163                        bytes[0] = papertape_font_line2byte(line);
     164                        bytes_n = 1;
     165                        // and tell the next loop that it has to read in.
     166                        new_character = 1;
     167                }
     168
     169                // Don't force to read in any more.
     170                if(last_ascii_id == ascii_id)
     171                        new_character = 0;
     172                last_ascii_id = ascii_id;
     173                strcpy(last_name, name);
     174        } // while file
     175       
     176        // we are done, and there's still one character left to push to the list
     177        if(last_ascii_id == 0)
     178                papertape_font_set_special(font, last_name, bytes, bytes_n);
     179        else
     180                papertape_font_set_char(font, last_ascii_id, bytes, bytes_n);
     181
     182        // rewind the file handle (if writing is called on the same file, etc.)
     183        rewind(fh);
     184}
     185
     186/**
     187 * Load Papertape font characters from binary array in order which is given
     188 * by the "schematics" string.
     189 *
     190 **/
     191void papertape_font_load_schematics(PAPERTAPE_FONT* font, const char *schematics, const byte_t *contents, int content_len) {
     192        int schematics_len = strlen(schematics);
     193        int schematics_x;
     194        int content_offset, content_start, content_end;
     195        for(schematics_x=0, content_offset=0; schematics_x < schematics_len; schematics_x++) {
     196                // find start of current character
     197                for(content_start = content_offset; content_start < content_len &&
     198                        contents[content_start] == 0; content_start++);
     199                // find end of current character
     200                for(content_end = content_start; content_end < content_len &&
     201                        contents[content_end] != 0; content_end++);
     202                // make new character between content_start and content_end
     203                papertape_font_set_char(font, schematics[schematics_x],
     204                        contents + content_start, content_end - content_start);
     205                // save offset for next character
     206                content_offset = content_end + 1;
     207        }
     208}
     209
     210
    98211
    99212/**
     
    105218        byte_t value = 0;
    106219        #define isPunched(pos, add) if(line[pos] != ' ') { value += add; }
    107         isPunched(0 ,  0);
     220        /* left border */
    108221        isPunched(1 ,  1);
    109222        isPunched(2,   2);
    110223        isPunched(3,   4);
    111         isPunched(4,   8);
    112         isPunched(5,  16);
    113         isPunched(6,  32);
    114         isPunched(7,  64);
    115         isPunched(8, 128);
     224        /* feed hole */
     225        isPunched(5,   8);
     226        isPunched(6,  16);
     227        isPunched(7,  32);
     228        isPunched(8,  64);
     229        isPunched(9, 128);
     230        /* right border */
    116231        return value;
    117232}
    118233
     234/**
     235 * This is the invert function of papertape_font_line2byte. It writes to
     236 * file handle "write" a line, but without Newline!
     237 *
     238 **/
     239void papertape_font_byte2line(const byte_t byte, FILE *write) {
     240        #define paintPunched(check) fputc((byte & check) ?\
     241                PAPERTAPE_FONT_WRITE_LOGICAL_1 : ' ', write)
     242        fputc(PAPERTAPE_FONT_BORDER, write); /* left border */
     243        paintPunched(  1); /* bit 1 */
     244        paintPunched(  2); /* bit 2 */
     245        paintPunched(  4); /* bit 3 */
     246        fputc(PAPERTAPE_FONT_FEED_HOLE, write); /* feed hole */
     247        paintPunched(  8); /* bit 4 */
     248        paintPunched( 16); /* bit 5 */
     249        paintPunched( 32); /* bit 6 */
     250        paintPunched( 64); /* bit 7 */
     251        paintPunched(128); /* bit 8 */
     252        fputc(PAPERTAPE_FONT_BORDER, write); /* right border */
     253}
     254
     255
     256/**
     257 * This function adds or changes special characters in the papertape font.
     258 * These characters are typically white spaces or word spacings.
     259 *
     260 * @param font The Papertape Font object, e.g. made by papertape_font_new_...()
     261 * @param name The Name ID
     262 * @param bytes Your byte array which is supposed to represent the character. The function copies it.
     263 * @param bytes_n The length of your byte array (counting from 1, of course)
     264 * @return 0 if successfully added, 1 otherwise. (It will never return 1 ;-) )
     265 **/
     266PAPERTAPE_FONT_CHAR* papertape_font_set_special(PAPERTAPE_FONT* font, const char *name, const byte_t* bytes, int bytes_n) {
     267        // allocate the new character structure
     268        PAPERTAPE_FONT_CHAR *buf;
     269        buf = malloc(sizeof(PAPERTAPE_FONT_CHAR));
     270
     271        // copy all values to structure
     272        buf->ascii_id = 0x0;
     273        buf->bytes_n = bytes_n;
     274        buf->bytes = malloc(bytes_n);
     275        memcpy(buf->bytes, bytes, bytes_n);
     276        buf->name = malloc(strlen(name));
     277        strcpy(buf->name, name);
     278       
     279        // Pack it at the very start
     280        buf->next = (font->next != NULL) ? font->next : NULL;
     281        font->next = buf;
     282
     283        return buf;
     284}
    119285
    120286/**
     
    122288 * Internally, the papertape_font works with a ascii ordered single linked list. So
    123289 * there's no other write access to that list than this function.
     290 *
     291 * If you want to set/add characters with special long names, use papertape_font_set_special_char.
    124292 *
    125293 * @param font The Papertape Font object, e.g. made by papertape_font_new_...()
     
    127295 * @param bytes Your byte array which is supposed to represent the character. The function copies it.
    128296 * @param bytes_n The length of your byte array (counting from 1, of course)
    129  * @return 0 if successfully added, 1 otherwise. (It will never return 1 ;-) )
    130  **/
    131 int papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n) {
     297 * @return A link to the generated character. You don't need it, anyway.
     298 **/
     299PAPERTAPE_FONT_CHAR* papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n) {
    132300        PAPERTAPE_FONT_CHAR *current;
    133301        PAPERTAPE_FONT_CHAR *next;
     
    142310        buf->bytes = malloc(bytes_n);
    143311        memcpy(buf->bytes, bytes, bytes_n);
     312        buf->name = NULL;
    144313        buf->next = NULL;
    145 
    146         // Magic ascii id (typically a non printable character) for character spacing
    147         if(ascii_id == PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID) {
    148                 font->bytes = buf->bytes;
    149                 font->bytes_n = buf->bytes_n;
    150                 free(buf); // don't need it.
    151                 return 0;
    152         }
    153314
    154315        // Start going throught the list
     
    162323                        current->next = buf;   // replace the next pointer with our char
    163324                        free(next);            // delete the old next pointer.
    164                         return 0;
     325                        return buf;
    165326                } else if(current->next->ascii_id > ascii_id) {
    166327                        // the next one has a bigger ascii_id as we have (perhapse
     
    170331                        current->next = buf;   // replace the next pointer with our char
    171332                        buf->next = next;      // pack the next pointer after our pointer
    172                         return 0;
     333                        return buf;
    173334                } else {
    174335                        // the next one has a lower ascii_id as we have, so we have
     
    181342        // or equal to ours. So we simply add our character to the list.
    182343        current->next = buf;
     344        return buf;
     345}
     346
     347/**
     348 * This is an auxillary function to access the linked list. It's not supposed to be used
     349 * from outside -- use highlevel functions like set_char or get_label.
     350 * @returns Link inside the list, NULL if not found.
     351 **/
     352PAPERTAPE_FONT_CHAR* papertape_font_low_get_char(PAPERTAPE_FONT* font, char ascii_id) {
     353        PAPERTAPE_FONT_CHAR* current;
     354        current = font;
     355        while(current->next != NULL) {
     356                if(current->next->ascii_id == ascii_id) {
     357                        // we have found the right character entry.
     358                        return current->next;
     359                } else
     360                        current = current->next;
     361        }
     362        return NULL;
     363}
     364
     365/**
     366 * This is just like papertape_font_low_get_char, but for special characters.
     367 * Ah, one special thing: Special characters are always valid, even if not defined.
     368 **/
     369PAPERTAPE_FONT_CHAR* papertape_font_low_get_special(PAPERTAPE_FONT* font, const char* name) {
     370        PAPERTAPE_FONT_CHAR* current;
     371        byte_t default_bytes[] = { 0 };
     372        current = font;
     373        while(current->next != NULL) {
     374                if(current->next->name != NULL &&
     375                   strcmp(current->next->name, name) == 0) {
     376                        // we have found the right character entry.
     377                        return current->next;
     378                } else
     379                        current = current->next;
     380        }
     381        // no special character found. We'll generate one.
     382        return papertape_font_set_special(font, name, default_bytes, 1);
     383}
     384
     385
     386/**
     387 * I don't know why one wanted to delete a character, but by using this function you can
     388 * do exactly that.
     389 * @returns 1 if successful deleted, 0 if not found in list.
     390 **/
     391int papertape_font_del_char(PAPERTAPE_FONT* font, char ascii_id) {
     392        // we cannot use papertape_font_low_get_char, because this would need
     393        // a double linked list.
     394        PAPERTAPE_FONT_CHAR* current;
     395        current = font;
     396        while(current->next != NULL) {
     397                if(current->next->ascii_id == ascii_id) {
     398                        // we have found the right character entry.
     399                        if(current->next->next != NULL) {
     400                                // we are somewhere in the list, so we have to rewrite
     401                                // the "next" pointer.
     402                                current->next = current->next->next;
     403                        }
     404                        free(current->next);
     405                        return 1;
     406                } else
     407                        current = current->next;
     408        }
     409        return 0;
     410}
     411
     412/**
     413 * This is the same like for normal characters, only for special characters.
     414 **/
     415int papertape_font_del_special(PAPERTAPE_FONT* font, const char* name) {
     416        PAPERTAPE_FONT_CHAR* current;
     417        current = font;
     418        while(current->next != NULL) {
     419                if(current->next->name != NULL &&
     420                   strcmp(current->next->name, name) == 0) {
     421                        // we have found the right character entry.
     422                        if(current->next->next != NULL) {
     423                                // we are somewhere in the list, so we have to rewrite
     424                                // the "next" pointer.
     425                                current->next = current->next->next;
     426                        }
     427                        free(current->next);
     428                        return 1;
     429                } else
     430                        current = current->next;
     431        }
    183432        return 0;
    184433}
     
    199448 *
    200449 **/
    201 byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string) {
    202         PAPERTAPE_FONT_CHAR* current;
    203         //int string_len = strlen(string);
    204         int x; // to count throught the string
    205         int size = 0; // the size of the output byte array
     450byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string, int* size) {
     451        PAPERTAPE_FONT_CHAR* character_spacing = papertape_font_low_get_special(font, PAPERTAPE_FONT_CHARACTER_SPACING_NAME);
     452        int x;       // to count throught the string
     453        *size = 0;   // the size of the output byte array
    206454        byte_t* output;
    207         int pos; // current position in output array
    208 
    209         // first step: Count the neccessary size for the output byte array
     455        int output_pos;  // current position in output array
     456        int clen = strlen(string)*2 -1; // length of characters stack array
     457        // dynamic stack arrays are ISO C99, compare http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Variable-Length.html
     458        PAPERTAPE_FONT_CHAR *characters[ clen ];
     459        int cpos = 0; // current position in character array
     460       
     461        // pack the font char array together
    210462        for(x=0; string[x] != '\0'; x++) {
    211                 // skip the font spacing at one character in the string
    212                 // (because it's only between the letters)
    213                 if(x != 0)
    214                         size += font->bytes_n;
    215 
    216                 // go throught the list and try to find the current character.
    217                 current = font;
    218                 while(1) {
    219                         if(current->ascii_id == string[x]) {
    220                                 size += current->bytes_n;
    221                                 break;
    222                         } else if(current->next != NULL) {
    223                                 current = current->next;
    224                                 continue;
    225                         } else {
    226                                 // We have reached the end of the character
    227                                 // list, but the current character is not available!
    228                                 fprintf(stderr, "papertape_font_get_label: Character \"%c\" in \"%s\" not printable\n",
    229                                         string[x], string);
    230                                 break;
    231                         }
     463                // find white spaces
     464                if(string[x] == ' ')
     465                        characters[cpos] = papertape_font_low_get_special(font, PAPERTAPE_FONT_WHITE_SPACE_NAME);
     466                else
     467                        characters[cpos] = papertape_font_low_get_char(font, string[x]);
     468                if(characters[cpos] == NULL) {
     469                        fprintf(stderr, "papertape_font_get_label: Character \"%c\" in \"%s\" not printable\n",
     470                                string[x], string);
     471                        continue;
     472                }
     473
     474                size += characters[cpos]->bytes_n;
     475                cpos++;
     476               
     477                // add the characters spacing after each character
     478                if(x+1 < strlen(string)) { // but not at the last character
     479                        characters[cpos] = character_spacing;
     480                        *size += characters[cpos]->bytes_n;
     481                        cpos++;
    232482                }
    233483        }
    234484
    235485        // now malloc the output byte array and generate it
    236         output = malloc(size);
    237         for(x=0,pos=0; string[x] != '\0'; x++) {
    238                 // go throught the list, another time
    239                 current = font;
    240                 while(1) {
    241                         if(current->ascii_id == string[x]) {
    242                                 memcpy(output+pos, current->bytes, current->bytes_n);
    243                                 pos += current->bytes_n;
    244                                 break;
    245                         } else if(current->next != NULL) {
    246                                 current = current->next;
    247                                 continue;
    248                         } // else: Character not found. Ignore it,
    249                         //   because error already printed at first run.
    250                 }
    251                 // add the character spacer after each character
    252                 if(x+1 < strlen(string)) { // but not at the last character
    253                         memcpy(output+pos, font->bytes, font->bytes_n);
    254                         pos += font->bytes_n;
    255                 }
    256         }
     486        output = malloc(*size);
     487        for(cpos=0,output_pos=0; cpos < clen; cpos++, output_pos += characters[cpos]->bytes_n)
     488                memcpy(output+output_pos, characters[cpos], characters[cpos]->bytes_n);
    257489
    258490        // we are done.
     
    269501 **/
    270502int papertape_font_string_is_printable(PAPERTAPE_FONT *font, const char* string) {
    271         PAPERTAPE_FONT_CHAR* current;
    272503        int x;
    273504
    274505        for(x=0; string[x] != '\0'; x++) {
    275                 current = font;
    276                 while(1) {
    277                         if(current->ascii_id == string[x])
    278                                 // this character is clean.
    279                                 break;
    280                         else if(current->next != NULL)
    281                                 // have not found character yet.
    282                                 current = current->next;
    283                         else
    284                                 // at the end of list -- character not found!
    285                                 return 0;
    286                 }
     506                if(string[x] == ' ')
     507                        // white spaces are always printable
     508                        continue;
     509                if(papertape_font_low_get_char(font, string[x]) == NULL)
     510                        // character not found!
     511                        return 0;
    287512        }
    288513
     
    304529        current = font;
    305530        while(1) {
    306                 // write index line
    307                 fprintf(fh, "%c %i\n", current->ascii_id, current->bytes_n);
    308                 // write data binary things.
     531                // write bytes and identifiers
    309532                for(x=0; x<current->bytes_n; x++) {
    310                         #define paintPunched(check) fputc((current->bytes[x] & check) ?\
    311                                 fputc(PAPERTAPE_FONT_WRITE_LOGICAL_1_CHARACTER, fh) : fputc(' ', fh), fh)
    312                         paintPunched(  0);
    313                         paintPunched(  1);
    314                         paintPunched(  2);
    315                         paintPunched(  4);
    316                         paintPunched(  8);
    317                         paintPunched( 16);
    318                         paintPunched( 32);
    319                         paintPunched( 64);
    320                         paintPunched(128);
     533                        papertape_font_byte2line(current->bytes[x], fh);
     534                        if(current->ascii_id == 0) {
     535                                fputs(PAPERTAPE_FONT_SPECIAL_ID, fh);
     536                                fputs(current->name, fh);
     537                        } else
     538                                fputc(current->ascii_id, fh);
    321539                        fputc('\n', fh);
    322540                }
    323 
     541       
    324542                // and go on. Or break, if at end.
    325                 if(current->next != NULL)
     543                if(current->next != NULL) {
     544                        // write a neat NULL byte as eye candy seperation
     545                        papertape_font_byte2line(0x0, fh);
     546                        fputc('\n', fh);
    326547                        current = current->next;
    327                 else
     548                } else
    328549                        break;
    329550        }
    330 }
    331 
    332 /* If we want it to be a standalone program */
     551       
     552        return 1; // could be void.
     553}
     554/**
     555 * This will print a dump of the papertape font linked list, just for debugging purpose,
     556 * to the file handle. It's just fine to give "stderr" at the file handle.
     557 **/
     558void papertape_font_dump(PAPERTAPE_FONT *font, FILE* fh) {
     559        PAPERTAPE_FONT_CHAR* current;
     560        int ram_bytes = 0;
     561        int i = 0;
     562        int x;
     563
     564        current = font;
     565        while(1) {
     566                ram_bytes += sizeof(PAPERTAPE_FONT_CHAR);
     567                if(current->ascii_id == 0 && current->name == NULL)
     568                        fprintf(fh, "%i. char: Special character, no name (broken), %i bytes allocated\n", i++, current->bytes_n);
     569                else if(current->ascii_id == 0) {
     570                        fprintf(fh, "%i. char: Special character, name \"%s\", %i bytes allocated\n", i++, current->name, current->bytes_n);
     571                        ram_bytes += strlen(current->name)+1;
     572                } else
     573                        fprintf(fh, "%i. char: \"%c\" (0x%x), %i bytes allocated\n", i++, current->ascii_id, current->ascii_id, current->bytes_n);
     574                if(current->bytes == NULL) {
     575                        fprintf(fh, "No bytes allocated!\n");
     576                } else {
     577                        ram_bytes += current->bytes_n;
     578                        for(x=0; x < current->bytes_n; x++) {
     579                                papertape_font_byte2line(current->bytes[x], fh);
     580                                fputc('\n', fh);
     581                        }
     582                }
     583               
     584                if(current->next == NULL) {
     585                        fprintf(fh, "End of list.\n");
     586                        fprintf(fh, "Statistics: %i entries, %i total bytes in heap\n", i, ram_bytes);
     587                        return;
     588                } else
     589                        current = current->next;
     590        }
     591}
     592
     593/**
     594 * The main() method (and auxillary helper functions) is only compiled when the switch
     595 * "STANDALONE" is defined. If not, we'll only get the simple library.
     596 *
     597 **/
    333598#ifdef STANDALONE
     599
     600/**
     601 * A helper method for the standalone main(): Read contents of a stream
     602 * in a dynamically allocated heap byte array.
     603 **/
    334604int file_get_contents(FILE *stream, byte_t **content) {
    335605        byte_t buf[4096];
     
    373643}
    374644
     645/**
     646 * The main method. We are using an own parameter system with extensive help messages.
     647 * No so much parameter flexibility, but it should be enough :-)
     648 **/
    375649int main(int argc, char **argv) {
    376         enum {
    377                 RENDER_TEXT,
    378                 WRITE_CHAR,
    379                 EXPORT,
    380                 IMPORT,
    381                 NOTHING
    382         } action;
    383         action = NOTHING;
    384         char *schematics = PAPERTAPE_FONT_SCHEMATICS_DEFAULT;
    385         char *render_text;
    386         char write_char;
    387         char *font_name;
    388         FILE *font_file;
    389         PAPERTAPE_FONT *font;
    390         int c;
     650        // Instead of poor getopt emulation, an own parameter system like "svn" CLI client has it
     651        if(argc < 3 || strcasecmp(argv[1], "help") == 0) {
     652                // argc == 1: Wrong call like "./font", missing subcommand.
     653                // Wrong call (like "./font label"), or help call (like "./font help").
     654                // Display help. Or startup help system.
     655                if(argc < 3) {
     656                        fprintf(stderr, "Usage: %s <subcommand> [subcommand option] <font file>\n"
     657                                "The Papertape font utility program.\n"
     658                                "Type '%s help <subcommand>' for help on a specific subcommand.\n"
     659                                "The <subcommand> and <font file> parameters are mandatory\n"
     660                                "\n"
     661                                "Available subcommands:\n"
     662                                "   help        Starts up this help system\n"
     663                                "   label       Prints out a complete label, given as the option\n"
     664                                "   set         Sets the character with the ASCII ID (e.g. 0x20), reads from STDIN\n"
     665                                "   del         Deletes the character with the ASCII ID from the font file\n"
     666                                "   dump        Parses the font file and prints out the result, for debugging\n"
     667                                "   export      Exports the font file to STDOUT, according to schematics\n"
     668                                "   import      Imports the font file, according to schematics\n"
     669                                "               Default schematics: %s\n",
     670                                argv[0], argv[0], PAPERTAPE_FONT_SCHEMATICS_DEFAULT);
     671                }
     672                               
     673                if(argc >= 3) {
     674                        // verbose Help system.
     675                        if(strcasecmp(argv[2], "label") == 0) {
     676                                fprintf(stderr, "label: Print out the compiled bytes for the translated label.\n"
     677                                                "Expects String as subcommand option.\n\n"
     678                                                "Example calls:\n"
     679                                                " Prints out yor text to STDOUT.\n"
     680                                                "   $ %s label \"The text you want to label\" your-font-file.ptf:\n"
     681                                                " The same with \"hello world\". Upper/lowercase produces the same result, btw.\n"
     682                                                "   $ %s label hello\\ world your-font-file.ptf:\n"
     683                                                " This writes the output to the file hallo-welt.bin:\n"
     684                                                "   $ %s label hallo\\ Welt your-font-file.ptf > hallo-welt.bin\n"
     685                                                " This will punch out all files in the directory with the right label before\n"
     686                                                "   $ for x in *; do %s label \"$x\" ../fonts/your.ptf | cat - \"$x\" | ./punch; done;\n",
     687                                                argv[0], argv[0], argv[0], argv[0]);
     688                        } else if(strcasecmp(argv[2], "set") == 0) {
     689                                fprintf(stderr, "set: Changes or adds a character in the font file\n"
     690                                                "Expects the wanted character as hex or character as subcommand option.\n\n"
     691                                                "Example calls:\n"
     692                                                " This will create a new font file that contains the character 'a':\n"
     693                                                "   $ cat how-a-should-look-like.bin | %s set a new-font-file.ptf\n"
     694                                                " This will overwrite the current value of the character 'a':\n"
     695                                                "   $ cat a-new-look-for-letter-a.bin | %s set a new-font-file.ptf\n"
     696                                                " This will set the blank space character:\n"
     697                                                "   $ echo -en \"\\0\" | %s set \" \" existing-font-file.ptf\n"
     698                                                " This will do exactly the same like above:\n"
     699                                                "   $ echo -en \"\\0\" | %s set 0x20 existing-font-file.ptf\n",
     700                                                argv[0], argv[0], argv[0], argv[0]);
     701                        } else if(strcasecmp(argv[2], "del") == 0) {
     702                                fprintf(stderr, "del: Deletes one or more characters from a font file\n"
     703                                                "Expects one character as subcommand option.\n\n"
     704                                                "Example calls:\n"
     705                                                " Delete a 'm' from your font file:\n"
     706                                                "   $ %s del m your-font-file.ptf\n",
     707                                                argv[0]);
     708                        } else if(strcasecmp(argv[2], "dump") == 0) {
     709                                fprintf(stderr, "dump: Parse a font file and print out the results to stdout. This is\n"
     710                                                "pracitcal for detecting syntax errors in the font file.\n\n"
     711                                                "Example calls:\n"
     712                                                "  Get a dump:\n"
     713                                                "   $ %s dump your-font-file.ptf\n"
     714                                                "  Save it somewhere:\n"
     715                                                "   $ %s dump your-font-file.ptf > dump.txt\n",
     716                                                argv[0], argv[0]);
     717                        } else if(strcasecmp(argv[2], "export") == 0|| strcasecmp(argv[2], "import") == 0) {
     718                                fprintf(stderr, "import/export: Converting font files to complete binary files on the fly\n"
     719                                                "You can give a scheme optionally as subcommand option.\n"
     720                                                "This is the default scheme:\n"
     721                                                "   %s\n\n"
     722                                                "Example calls:\n"
     723                                                " Export an existing font file, edit the file with an hex editor and import it again:\n"
     724                                                "   $ %s export your-font-file.ptf > font-file-export.bin\n"
     725                                                "   $ khexdump font-file-export.bin\n"
     726                                                "          [ some GUI editing, and so on...]\n"
     727                                                "   $ cat font-file-export.bin | %s import your-font-file.ptf\n"
     728                                                " Import to a new font file from existin g other font mechanism:\n"
     729                                                "   $ SCHEME=\"abcdefgh...4567890 ()<>\"\n"
     730                                                "   $ perl another-font.pl \"$SCHEME\" | %s import \"%s\" importet-font-file.ptf\n"
     731                                                " Make a new font file which only contains the digits of the old file:\n"
     732                                                "   $ %s export \"0123456789\" existing.ptf | %s import \"0123456789\" new.ptf\n",
     733                                                PAPERTAPE_FONT_SCHEMATICS_DEFAULT, argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]);
     734                        } else {
     735                                fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n",
     736                                        argv[2], argv[0]);
     737                        }
     738                } // verbose help
     739                return 0;
     740        } // Help messages
     741
     742        if(strcasecmp(argv[1], "export") == 0 || strcasecmp(argv[1], "label") == 0) {
     743                // export or label.
     744                int size;
     745                byte_t *bytes;
     746                PAPERTAPE_FONT *font;
     747                 
     748                if(! (font=papertape_font_new_from_file(argc == 4 ? argv[3] : argv[2]) )) {
     749                        fprintf(stderr, "Error creating Papertape font.\n");
     750                        return 3;
     751                }
     752
     753                bytes = papertape_font_get_label(font, argc == 4 ? argv[3] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT, &size);
     754                fwrite(bytes, size, sizeof(byte_t), stdout);
     755                return 0;
     756        } else if(strcasecmp(argv[1], "set") == 0 || strcasecmp(argv[1], "del") == 0) {
     757                // set and delete
     758                char character;
     759                FILE *font_file;
     760                PAPERTAPE_FONT *font;
     761               
     762                // at first: get the character
     763                if(argc == 3) {
     764                        fprintf(stderr, "%s: Too few arguments! Missing character. Type '%s help %s' for usage.\n",
     765                                argv[1], argv[0], argv[1]);
     766                        return 1;
     767                }
     768                if(strlen(argv[2]) > 1)
     769                        // A Number like "255", 0x20 or 080. strol will parse it.
     770                        character = (char)strtol(argv[2], NULL, 0);
     771                else
     772                        // only one character in ASCII.
     773                        character = argv[2][0];
     774
     775                // open file for reading and WRITING.
     776                font_file = fopen(argv[3], "r+");
     777                if(!font_file) {
     778                        perror("Opening font file for writing");
     779                        return 2;
     780                }
     781                font = papertape_font_new();
     782                papertape_font_load_file(font, font_file);
     783
     784                if(strcasecmp(argv[1], "set")) {
     785                        // change/add character
     786                        byte_t *contents;
     787                        int bytes = file_get_contents(stdin, &contents);
     788                        printf("Read in %i bytes for letter %c (seen as byte: 0x%x)\n", bytes, character, character);
     789                        papertape_font_set_char(font, character, contents, bytes);
     790                        printf("Set character %c successfully.\n", character);
     791                } else {
     792                        // delete character
     793                        printf(papertape_font_del_char(font, character) ?
     794                                "Deleted %c successfully from file %s\n" :
     795                                "Could not find %c in file %s!\n",
     796                                character, argv[3]);
     797                }
     798               
     799                // write papertape file.
     800                papertape_font_write_to_file(font, font_file);
     801                return 0;
     802        } else if(strcasecmp(argv[1], "dump") == 0) {
     803                PAPERTAPE_FONT* font = papertape_font_new_from_file(argv[2]);
     804                printf("This is a dump from the internal single linked ordered list,\n"
     805                        "created from the file '%s':\n\n", argv[2]);
     806                papertape_font_dump(font, stdout);
     807                return 0;
     808        } else if(strcasecmp(argv[1], "import") == 0) {
     809                // run an import.
     810                PAPERTAPE_FONT *font = papertape_font_new();
     811                FILE* font_file;
     812                byte_t *contents;
     813                int content_len;
     814                char *schematics;
     815               
     816                // open file for DESTROYING WRITING.
     817                font_file = fopen(argc == 4 ? argv[3] : argv[2], "w");
     818                if(font_file == NULL) {
     819                        perror("Opening font file for writing");
     820                        return 2;
     821                }
     822               
     823                schematics = argc == 4 ? argv[2] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT;
     824
     825                fprintf(stderr, "Reading from stdin, expecting schematics %s\n", schematics);
     826                content_len = file_get_contents(stdin, &contents);
     827                // debug: print it out!
     828                // fwrite(contents, 1, content_len, stdout); exit(0);
    391829       
    392         // poor man's getopt, for windows portability
    393         for(c=1; c<argc; c++) {
    394                 if(strlen(argv[c]) == 2 && *argv[c] == '-') {
    395                         argv[c]++; // shift "-" flag thingy.
    396                         switch(*argv[c]) {
    397                                 case 'h':
    398                                         fprintf(stderr, "Usage: %s [OPTIONS...] [A PAPERTAPE FONT FILE]\n"
    399                                                 "The Papertape font utilitie program.\n\n"
    400                                                 "   optional parameters:\n"
    401                                                 "   -h              display this help message\n"
    402                                                 "   -l [TEXT]       Render this text to STDOUT\n"
    403                                                 "   -w [ASCII ID]   Set the character with that ASCII ID, read from STDIN\n"
    404                                                 "   -o              Export the font file, according to schematics, to STDOUT\n"
    405                                                 "   -i              Import to font file (overwrite it), according to schematics, from STDIN\n\n"
    406                                                 "   -s [Sch string] Define own schematics for -i/-o. Default is:\n"
    407                                                 "                   %s\n",
    408                                                 argv[0], PAPERTAPE_FONT_SCHEMATICS_DEFAULT);
    409                                         return 0;
    410 
    411                                 case 'l':
    412                                         action = RENDER_TEXT;
    413                                         render_text = argv[++c];
    414                                         break;
    415                                
    416                                 case 'w':
    417                                         action = WRITE_CHAR;
    418                                         write_char = *argv[++c];
    419                                         break;
    420                                        
    421                                 case 'o': action = EXPORT; break;
    422                                 case 'i': action = IMPORT;  break;
    423                                
    424                                 case 's':
    425                                         schematics = argv[++c];
    426                         } // switch
    427                 } // if flag
    428                 else {
    429                         // not flag but... Papertape Font file!
    430                         font_name = argv[c];
    431                 }
    432         } // for each argv
    433 
    434         // Open Font File
    435         if(font_name) {
    436                 char *mode;
    437                 switch(action) {
    438                         case RENDER_TEXT:
    439                         case EXPORT:       mode = "r";  break;
    440                         case WRITE_CHAR:   mode = "rw"; break;
    441                         case IMPORT:       mode = "w";  break;
    442                         default:
    443                                 fprintf(stderr, "Error: No action specified. Call %s -h\n", argv[0]);
    444                                 return 1;
    445                 }
    446        
    447                 font_file = fopen(font_name, mode);
    448                 if(!font_file) {
    449                         perror("Opening font file");
    450                         return 2;
    451                 }
     830                // Parse schematics
     831                papertape_font_load_schematics(font, schematics, contents, content_len);
     832
     833                fprintf(stderr, "Readed from stdin successfully. Here you get a dump:\n");
     834                papertape_font_dump(font, stdout);
     835
     836                // that's it, we are done. Write the papertape font.
     837                papertape_font_write_to_file(font, font_file);
     838                return 0;
    452839        } else {
    453                 fprintf(stderr, "Error: No font file specified. Call %s -h\n", argv[0]);
    454         }
    455        
    456         // Stupid: papertapefont.c:431: Warnung: Zuweisung erzeugt Zeiger von Ganzzahl ohne Typkonvertierung
    457         // Adding (PAPERTAPE_FONT*) did the job.
    458         font = (PAPERTAPE_FONT*) papertape_new_from_file(font_file);
    459         if(font == NULL) {
    460                 fprintf(stderr, "Error creating PAPERTAPE_FONT object\n");
    461                 return 3;
    462         }
    463 
    464         // Perform action
    465         if(action == RENDER_TEXT && render_text != NULL) {
    466                 papertape_font_get_label(font, render_text);
    467         } else if(action == WRITE_CHAR) {
    468                 byte_t *contents;
    469                 int bytes = file_get_contents(stdin, &contents);
    470                 printf("Read in %i bytes for letter %c (seen as byte: 0x%x)\n", bytes, write_char, write_char);
    471                 papertape_font_set_char(font, write_char, contents, bytes);
    472         } else if(action == EXPORT) {
    473                 fprintf(stderr, "Printing file %s with schematics %s\n", font_name, schematics);
    474                 puts(papertape_font_get_label(font, schematics));
    475         } else if(action == IMPORT) {
    476                 byte_t *contents;
    477                 int bytes, x;
    478                
    479                 fprintf(stderr, "Reading from stdin, expecting schematics %s\n", schematics);
    480                 bytes = file_get_contents(stdin, &contents);
    481                
    482                 fprintf(stderr, "To be implemented...\n");
    483                
    484                 // Parse schematics
    485                 /*for(x=0; x<strlen(schematics); x++) {
    486                         papertape_font_set_char(font, schematics[x],
    487                 }*/
    488                
    489         } else {
    490                 fprintf(stderr, "No correct call! See %s -h\n", argv[0]);
    491         }
    492         return 0;
    493 }
     840                fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n",
     841                        argv[2], argv[0]);
     842                return 1;
     843        }
     844} // main
    494845#endif /* STANDALONE */
  • schriften/papertapefont.h

    r12 r13  
    2929#include <stdio.h>
    3030
     31/**
     32 * We use "char" in three situations:
     33 *   1. As normal (ASCII) characters/letters, like in    char ascii_id = 'a';
     34 *   2. As strings, like in                             char* line = "hello";
     35 *   3. As byte arrays, like in  unsigned char *bytes = { 0x12, 0x23, 0x45 };
     36 *
     37 * To make this a bit more clearer, here is a quite common typedef:
     38 **/
    3139typedef unsigned char   byte_t;
    3240
     41/**
     42 * This is the structure of one link from the linked list. It represents exactly
     43 * one character, how it is punched on the papertape. It's called
     44 * PAPERTAPE_FONT_CHAR everywhere in the program, by using a typedef.
     45 *
     46 * The PAPERTAPE_FONT struct itself is nothing else than the start element of the
     47 * linked list. Therefore it's just the same data structure.
     48 **/
    3349struct _papertape_font_char {
    3450        char ascii_id;
     51        char *name;
    3552        byte_t* bytes;
    3653        int bytes_n;
     
    4259typedef struct _papertape_font_char PAPERTAPE_FONT;
    4360
    44 #define PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID  '\t'
    45 static byte_t PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES[] = { 0x00 };
     61/**
     62 * These defines are the names which are used in the Papertape font files
     63 * for the specific special characters.
     64 **/
     65#define PAPERTAPE_FONT_CHARACTER_SPACING_NAME   "character_spacing"
     66#define PAPERTAPE_FONT_WHITE_SPACE_NAME         "white_space"
    4667
     68/**
     69 * The content of this definition is written at the very first lines at every
     70 * papertape font file which is changed by the routines.
     71 *
     72 **/
    4773#define PAPERTAPE_FONT_WRITE_FIRST_LINES \
    48         "# The Paper Tape Project -- PAPERTAPE_FONT file.\n" \
    49         "# The content of this file describes a paper tape font. You can edit it\n" \
    50         "# with a text editor. Lines like that ones are comments. They are not allowed\n" \
    51         "# in data binary lines.\n" \
    52         "# See http://dev.technikum29.de/svn/paper-tape-project/\n"
     74        "The Paper Tape Project -- PAPERTAPE_FONT file.\n" \
     75        "The content of this file describes a paper tape font. You can edit it\n" \
     76        "with a text editor. Lines like this ones are comments.\n" \
     77        "See http://dev.technikum29.de/svn/paper-tape-project/\n" \
     78        "See this small key how the document is structured:"\
     79        "\n" \
     80        " bits     |identification\n" \
     81        " 123.45678|char/string\n" \
     82        "|   .     |\n"
    5383
    54 #define PAPERTAPE_FONT_WRITE_LOGICAL_1_CHARACTER   '+'
     84/**
     85 * These definitions define characters which are used when writing papertape
     86 * files. They should not be changed unless the data format changes one day.
     87 **/
     88#define PAPERTAPE_FONT_WRITE_LOGICAL_1   '+'
     89#define PAPERTAPE_FONT_FEED_HOLE         '.'
     90#define PAPERTAPE_FONT_BORDER            '|'
     91/**
     92 * This is the magic id. It's quite very important.
     93 *
     94 **/
     95#define PAPERTAPE_FONT_SPECIAL_ID        (" =")
    5596
     97/**
     98 * The default schematics used for import and export. It should contain
     99 * the most important characters, like /[0-9a-z]/i (as regex)
     100 **/
    56101#define PAPERTAPE_FONT_SCHEMATICS_DEFAULT "abcdefghijklmnopqrstuvwxyz0123456789"
    57102
    58 PAPERTAPE_FONT* papertape_font_new_from_file(FILE* fh);
    59 //PAPERTAPE_FONT* papertape_font_new_from_string(const char* string);
    60103
    61 // Read only functions
    62 byte_t *papertape_font_get_label(PAPERTAPE_FONT* font, const char* string);
     104// Create and load functions
     105#define papertape_font_new()    (malloc(sizeof(PAPERTAPE_FONT)))
     106PAPERTAPE_FONT* papertape_font_new_from_file(const char* filename);
     107void papertape_font_load_file(PAPERTAPE_FONT* font, FILE* fh);
     108void papertape_font_load_schematics(PAPERTAPE_FONT* font, const char *schematics, const byte_t *contents, int content_len);
     109void papertape_font_destroy(PAPERTAPE_FONT* font);
     110
     111
     112// Read functions
     113byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string, int *size);
    63114int papertape_font_string_is_printable(PAPERTAPE_FONT *font, const char* string);
     115void papertape_font_dump(PAPERTAPE_FONT *font, FILE* fh);
     116PAPERTAPE_FONT_CHAR* papertape_font_low_get_char(PAPERTAPE_FONT* font, char ascii_id);
     117PAPERTAPE_FONT_CHAR* papertape_font_low_get_special(PAPERTAPE_FONT* font, const char* name);
    64118
    65119// Write functions
    66 int papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const unsigned char* bytes, int bytes_n);
    67 #define papertape_font_set_char_spacing(font, bytes, bytes_n) \
    68         (papertape_font_set_char(font, PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID, bytes, bytes_n))
     120PAPERTAPE_FONT_CHAR* papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n);
     121PAPERTAPE_FONT_CHAR* papertape_font_set_special(PAPERTAPE_FONT* font, const char* name, const byte_t* bytes, int bytes_n);
     122int papertape_font_del_char(PAPERTAPE_FONT* font, char ascii_id);
     123int papertape_font_del_special(PAPERTAPE_FONT* font, const char* name);
    69124int papertape_font_write_to_file(PAPERTAPE_FONT* font, FILE *fh);
    70125
Note: See TracChangeset for help on using the changeset viewer.
© 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