/* * * Input procedures. * */ #include "resconv.h" #include "extdef.h" #include typedef struct SrcFile { HANDLE fnum; /* file handle */ long size; /* file size */ long total_read; /* total bytes read from file */ uchar ptr buffer; /* input data buffer */ long bytes; /* # bytes in data buffer */ long bytes_read; /* # bytes read from buffer */ } SrcFile; #define max_src_files 10 SrcFile srcTable [max_src_files]; /* source file table */ long src_entries; /* # entries in source file table */ SrcFile ptr srcFile; /* the current source file element */ typedef struct Token { ushort token; /* token type */ long value; /* if numeric token */ uchar ptr string; /* if string, constant, or filename token */ } Token; static Token token; /* the current token */ static Token putToken; /* the put back token */ static uchar putChar; /* the put back character */ extern ushort cLineCurrent; /* the current line number */ static uchar ptr pszComment = NULL; /* the current comment line. */ static ushort iComment = 0; /* current place in comment string. */ void debug_ptoken() { switch(token.token) { case tok_numeric: printf( "Numeric Token (%d)\n", token.value); break; case tok_string: printf( "Numeric Token (%s)\n", token.string); break; case tok_constant: printf( "Constant Token (%d)\n", token.value); break; case tok_undefined: printf( "Undef Token\n"); break; case tok_keyword: printf( "Keyword Token\n"); break; case tok_filename: printf( "File Name Token (%s)\n", token.string); break; case tok_comma: printf( "COMMA Token\n"); break; case tok_bitor: printf( "BITOR Token\n"); break; case tok_plus: printf( "PLUS Token\n"); break; case tok_pound_sign: printf( "POUND Token\n"); break; case tok_eof: printf( "EOF Token\n"); break; } } /************************************************************************** * * * READ_DATA * * * * This procedure is called to read some data from a file. * * * **************************************************************************/ static flag read_data (SrcFile ptr src) { ushort error; DWORD read; /* Read the data, and die if we get an error. */ src->bytes = MIN ((long) max_buffer_bytes, src->size - src->total_read); error = ReadFile (src->fnum, src->buffer, src->bytes, &read, NULL); if ((error == FALSE) || (ushort) src->bytes != read) { printf ("I/O error %d while reading from data file.\n", GetLastError()); close_destination_file (); terminate_input (); exit(1); } /* Success is ours. */ src->bytes_read = 0L; src->total_read += src->bytes; return TRUE; } /*********************************************************************** * * * PUT_CHAR * * * * This procedure puts a character back into the input stream. * * * ***********************************************************************/ void put_char (uchar ch) { /* If we already have a put back character, there's a logic error. */ if (putChar) report_error (err_full_put_char_buffer, 0, "put_char"); /* Save the character. */ putChar = ch; } /*********************************************************************** * * * GET_CHAR * * * * This procedure gets the next character from the input stream. * * * ***********************************************************************/ uchar get_char (void) { uchar ch; /* character */ /* If there is a put back character, return it. */ if (putChar) { ch = putChar; putChar = 0; return ch; } /* Make sure we have a current source file element */ if (srcFile == NULL) report_error (err_no_source_file, 0, "get_char"); /* * While there are still entries in the source file table, try to * get a character from a source file. */ while (src_entries > 0) { /* * See if there are more characters in the file buffer. */ if (srcFile->bytes > srcFile->bytes_read) return (srcFile->buffer [srcFile->bytes_read++]); /* * If there are more bytes in the file to be read, read into the buffer. */ if (srcFile->size > srcFile->total_read) { read_data (srcFile); return (srcFile->buffer [srcFile->bytes_read++]); } /* * We reached the end of the current source file, so delete it from * the table and get the next entry. */ CloseHandle(srcFile->fnum); --src_entries; if (src_entries > 0) srcFile = &srcTable [src_entries - 1]; else srcFile = NULL; } /* Couldn't find anything to return so we must be at the EOF. */ return char_eof; } /*********************************************************************** * * * ALPHA * * * * This procedure checks to see if a character is an alpha character. * * * ***********************************************************************/ static flag alpha (uchar ch) { return (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z'); } /*********************************************************************** * * * NUMERIC * * * * This procedure checks to see if a character is a numeric character.* * * ***********************************************************************/ static flag numeric (uchar ch) { return (ch >= '0' && ch <= '9'); } /*********************************************************************** * * * WHITE_SPACE * * * * This procedure checks if a character is white space. * * * ***********************************************************************/ static flag white_space (uchar ch) { /* Switch on the character and return the result. */ switch (ch) { case char_cr: case char_lf: case char_space: case char_tab: return TRUE; break; case char_eof: default: return FALSE; break; } return FALSE; } /*********************************************************************** * * * SKIP_COMMENT * * * * This procedure skips comment text. * * * ***********************************************************************/ static void skip_comment (void) { uchar ch, last_ch; /* See if the next character is a '*'. If so, handle it. ====================================================== */ if ((ch = get_char ()) == char_star) { /* Make sure we have a comment buffer. =================================== */ if (pszComment == NULL) pszComment = _fmalloc (max_comment_size); /* Start filling in the comment buffer. ==================================== */ iComment = 0; if (pszComment) { pszComment[0] = char_slash; pszComment[1] = char_star; iComment = 2; } /* Loop until we see a '/'. */ forever { while ((ch = get_char ()) != char_slash && ch != char_eof) { last_ch = ch; if (ch == char_cr) cLineCurrent++; if (pszComment) pszComment[iComment++] = ch; } if (pszComment && ch != char_eof) pszComment[iComment++] = ch; /* If we reached the end of the file, there was an error. */ if (ch == char_eof) { iComment = 0; report_error (err_unexpected_eof, 0, "skip_comment"); } /* If the last character was a '*', this is the end of the comment. Otherwise, continue. =================================================== */ if (last_ch == char_star) { if (pszComment) pszComment[iComment] = 0; return; } last_ch = ch; } return; // should never execute this line? } /* Handle the two slash comment format. ==================================== */ else if (ch == char_slash) { /* Make sure we have a comment buffer. =================================== */ if (pszComment == NULL) pszComment = _fmalloc (max_comment_size); /* Start filling in the comment buffer. ==================================== */ iComment = 0; if (pszComment != NULL) { pszComment[0] = char_slash; pszComment[1] = char_slash; iComment = 2; } /* Skip to first character on next line. */ while ((ch = get_char ()) != char_eof) { if (pszComment) pszComment[iComment++] = ch; if (ch == char_lf) { if (pszComment) { iComment -= 2; // Don't keep the cr-lf. pszComment[iComment] = 0; } return; } } /* If we reach here, end-of-file. Put eof back. */ put_char(char_eof); return; } /* Otherwise, unexpected slash character. ====================================== */ else report_error (err_unexpected_comment_char, 0, "skip_comment"); return; } /*********************************************************************** * * * SKIP_WHITE_SPACE * * * * This procedure skips white space characters including comments. * * * ***********************************************************************/ static void skip_white_space (void) { uchar ch; /* Loop forever getting characters until a non-white space char is found. */ forever { ch = get_char (); if (ch == char_cr) cLineCurrent++; if (ch == char_slash) skip_comment (); else if (not white_space (ch) || ch == char_eof) { put_char (ch); return; } } } /*********************************************************************** * * * LEX_STRING * * * * This procedure gets a token that is a string. * * * ***********************************************************************/ static void lex_string (void) { uchar ptr buffer; /* string buffer */ ushort i; /* loop counter */ uchar ch; /* character */ /* Get a buffer large enough to hold the maximum number of chars. */ buffer = _fmalloc (max_string_size); for (i = 0; i < max_string_size; i++) { /* * Get a character and exit now if the EOF was reached. */ ch = get_char (); if (ch == char_eof) report_error (err_unexpected_eof, 0, "lex_string"); /* * If it's not the delimiter quote put the glob in the buffer. */ if (ch == char_cr) cLineCurrent++; if (ch != char_quote_double) buffer [i] = ch; /* * Else it's the delimiter quote but if it's not followed by another, * this must be the end of the string. */ else { ch = get_char (); if (ch != char_quote_double) { put_char (ch); break; } buffer [i] = ch; } } /* Make sure the string isn't too long. */ if (i >= max_string_size) report_error (err_string_too_long, 0, "lex_string"); /* Save the string information in the current token. */ buffer [i] = '\0'; token.token = tok_string; token.string = buffer; } /*********************************************************************** * * * LEX_KEYWORD * * * * This procedure checks is a token is a keyword. * * * ***********************************************************************/ flag lex_keyword (uchar ptr buffer) { ushort tok_id; /* token identifier */ ushort i; /* loop counter */ typedef struct { ushort id; /* numeric ID */ uchar *text; /* text ID */ } KeywordTable; static KeywordTable keyword_table [] = { tok_acceltable , "ACCELTABLE", tok_alt , "ALT", tok_begin , "BEGIN", tok_bitmap , "BITMAP", tok_bs_autocheckbox , "BS_AUTOCHECKBOX", tok_bs_autoradiobutton , "BS_AUTORADIOBUTTON", tok_bs_checkbox , "BS_CHECKBOX", tok_bs_default , "BS_DEFAULT", tok_bs_help , "BS_HELP", tok_bs_nopointerfocus , "BS_NOPOINTERFOCUS", tok_bs_pushbutton , "BS_PUSHBUTTON", tok_bs_radiobutton , "BS_RADIOBUTTON", tok_cbs_dropdown , "CBS_DROPDOWN", tok_cbs_dropdownlist , "CBS_DROPDOWNLIST", tok_cbs_simple , "CBS_SIMPLE", tok_char , "CHAR", tok_control , "CONTROL", tok_dialog , "DIALOG", tok_discardable , "DISCARDABLE", tok_dlgtemplate , "DLGTEMPLATE", tok_dt_bottom , "DT_BOTTOM", tok_dt_center , "DT_CENTER", tok_dt_left , "DT_LEFT", tok_dt_mnemonic , "DT_MNEMONIC", tok_dt_right , "DT_RIGHT", tok_dt_top , "DT_TOP", tok_dt_vcenter , "DT_VCENTER", tok_dt_wordbreak , "DT_WORDBREAK", tok_end , "END", tok_es_autoscroll , "ES_AUTOSCROLL", tok_es_center , "ES_CENTER", tok_es_left , "ES_LEFT", tok_es_margin , "ES_MARGIN", tok_es_right , "ES_RIGHT", tok_fcf_sysmenu , "FCF_SYSMENU", tok_fcf_titlebar , "FCF_TITLEBAR", tok_fixed , "FIXED", tok_frame , "FRAME", tok_fs_border , "FS_BORDER", tok_fs_dlgborder , "FS_DLGBORDER", tok_fs_mousealign , "FS_MOUSEALIGN", tok_fs_nobytealign , "FS_NOBYTEALIGN", tok_icon , "ICON", tok_include , "INCLUDE", tok_loadoncall , "LOADONCALL", tok_ls_horzscroll , "LS_HORZSCROLL", tok_ls_multiplesel , "LS_MULTIPLESEL", tok_menu , "MENU", tok_menuitem , "MENUITEM", tok_mia_disabled , "MIA_DISABLED", tok_mis_bitmap , "MIS_BITMAP", tok_mis_break , "MIS_BREAK", tok_mis_ownerdraw , "MIS_OWNERDRAW", tok_mis_text , "MIS_TEXT", tok_moveable , "MOVEABLE", tok_pointer , "POINTER", tok_preload , "PRELOAD", tok_rcinclude , "RCINCLUDE", tok_resource , "RESOURCE", tok_sbs_horz , "SBS_HORZ", tok_sbs_vert , "SBS_VERT", tok_separator , "SEPARATOR", tok_shift , "SHIFT", tok_stringtable , "STRINGTABLE", tok_ss_fgndframe , "SS_FGNDFRAME", tok_ss_groupbox , "SS_GROUPBOX", tok_ss_halftoneframe , "SS_HALFTONEFRAME", tok_ss_text , "SS_TEXT", tok_submenu , "SUBMENU", tok_virtualkey , "VIRTUALKEY", wc_button , "WC_BUTTON", wc_combobox , "WC_COMBOBOX", wc_entryfield , "WC_ENTRYFIELD", wc_listbox , "WC_LISTBOX", wc_scrollbar , "WC_SCROLLBAR", wc_static , "WC_STATIC", tok_ws_clipsiblings , "WS_CLIPSIBLINGS", tok_ws_group , "WS_GROUP", tok_ws_savebits , "WS_SAVEBITS", tok_ws_tabstop , "WS_TABSTOP", tok_ws_visible , "WS_VISIBLE", tok_dt_halftone , "DT_HALFTONE", tok_fcf_nobytealign , "FCF_NOBYTEALIGN", tok_dlginclude , "DLGINCLUDE", tok_ls_ownerdraw , "LS_OWNERDRAW", wc_mle , "WC_MLE", tok_mls_vscroll , "MLS_VSCROLL", tok_mls_wordwrap , "MLS_WORDWRAP", tok_helptable , "HELPTABLE", tok_helpsubtable , "HELPSUBTABLE", tok_mls_border , "MLS_BORDER", tok_syscommand , "SYSCOMMAND", tok_mis_syscommand , "MIS_SYSCOMMAND", tok_mis_submenu , "MIS_SUBMENU", tok_ss_icon , "SS_ICON", tok_did_ok , "DID_OK", tok_did_cancel , "DID_CANCEL", tok_fs_icon , "FS_ICON", tok_begin , "{", tok_end , "}", tok_mia_checked , "MIA_CHECKED", tok_fcf_dlgborder , "FCF_DLGBORDER", tok_mis_separator , "MIS_SEPARATOR", tok_ltext , "LTEXT", tok_rtext , "RTEXT", tok_ctext , "CTEXT", tok_radiobutton , "RADIOBUTTON", tok_autoradiobutton , "AUTORADIOBUTTON", tok_checkbox , "CHECKBOX", tok_autocheckbox , "AUTOCHECKBOX", tok_pushbutton , "PUSHBUTTON", tok_defpushbutton , "DEFPUSHBUTTON", tok_listbox , "LISTBOX", tok_groupbox , "GROUPBOX", tok_entryfield , "ENTRYFIELD", tok_fcf_minbutton , "FCF_MINBUTTON", tok_fcf_icon , "FCF_ICON", tok_mis_breakseparator , "MIS_BREAKSEPARATOR", tok_mis_buttonseparator , "MIS_BUTTONSEPARATOR", 0 , "", }; /* Search the keyword table for a match. */ tok_id = 0; for (i = 0; tok_id == 0 && keyword_table [i].id != 0; i++) { if (equal_ignoring_case (buffer, keyword_table [i].text)) tok_id = keyword_table [i].id; } // Printf error message for unsupported items if (tok_id == tok_mis_bitmap) { printf(" BITMAP menu are not supported by RESCONV. \n\r Define them as text menu and then use Windows ModifyMenu in your program\n\r"); } /* Update the current token structure and return. */ if (tok_id == 0) { return FALSE; } else { token.token = tok_id; return (TRUE); } } /*********************************************************************** * * * LEX_ALPHA * * * * This procedure gets a token that starts with an alpha character. * * * ***********************************************************************/ static void lex_alpha (uchar ch) { uchar ptr buffer; /* string buffer */ ushort i; /* loop counter */ /* Fill the buffer with the alpha/numeric characters. */ buffer = _fmalloc (max_identifier_size + 1); buffer [0] = ch; for (i = 1; i <= max_identifier_size; i++) { ch = get_char (); if (white_space (ch) || ch == char_comma || ch == char_pipe) { put_char (ch); break; } buffer [i] = ch; } /* See if the identifier is longer than allowable. */ if (i > max_identifier_size) report_error (err_identifier_too_long, 0, "lex_alpha"); /* See if this is a keyword. */ buffer [i] = '\0'; if (lex_keyword (buffer)) { _ffree (buffer); return; } /* Save the string as a constant. */ token.string = buffer; token.token = tok_constant; } /*********************************************************************** * * * LEX_NUMERIC * * * * This procedure gets a token that starts with a numeric character. * * * ***********************************************************************/ static void lex_numeric (uchar ch, flag sign) { uchar buffer [40]; /* value buffer */ uchar ptr sp; /* string pointer */ long value; /* numeric value */ ushort i; /* loop counter */ /* Build a buffer of characters until a non-numeric character is found. */ sp = buffer; if (sign) *sp++ = '-'; *sp++ = ch; for (i = 0; i < ucharsizeof (buffer); i++) { if (!numeric (ch = get_char ())) { put_char (ch); break; } *sp++ = ch; } /* Make sure the number isn't too many characters. */ if (i >= ucharsizeof (buffer)) report_error (err_value_too_big, 0, "lex_numeric"); /* First check to see if this is an integer. */ *sp = '\0'; value = atol (buffer); token.token = tok_numeric; token.value = value; } /*********************************************************************** * * * LEX_SPECIAL * * * * This procedure gets a token that starts with a special character. * * * ***********************************************************************/ static void lex_special (uchar ch) { ushort tok; /* Switch on the character to determine the token type. */ switch (ch) { case char_quote_double: lex_string (); return; break; case char_minus: if (numeric (ch = get_char ())) lex_numeric (ch, TRUE); else report_error (err_invalid_syntax, 0, "lex_special"); return; break; case char_plus: tok = tok_plus; break; case char_comma: tok = tok_comma; break; case char_pipe: tok = tok_bitor; break; case char_bracket_begin: tok = tok_begin; break; case char_bracket_end: tok = tok_end; break; case char_less_than: case char_back_slash: case char_dot: /* * This must be a file name. */ lex_alpha (ch); return; break; case char_pound_sign: tok = tok_pound_sign; break; case char_eof: tok = tok_eof; break; default: report_error (err_invalid_character, 0, "lex_special"); return; break; } /* Update the current token structure and return. */ token.token = tok; } /*********************************************************************** * * * GET_TOKEN * * * * This procedure gets the next token from the input string. * * * ***********************************************************************/ ushort get_token (void) { uchar ch; /* a character */ uchar buffer [max_identifier_size + 1]; ushort index; /* If a token has been put back, just get that one. ================================================ */ if (putToken.token != tok_undefined) { token = putToken; putToken.token = tok_undefined; if (is_debug_on) debug_ptoken(); return token.token; } /* Skip any white space and clean up the current token. ==================================================== */ skip_white_space (); if (token.string) _ffree (token.string); token.token = 0; token.value = 0L; token.string = NULL; /* Get a character and see if it's alphabetic, numeric, or special. It's not a cr, which would have been eaten in skip_white_space. ================================================================ */ forever { ch = get_char (); if (alpha (ch)) lex_alpha (ch); else if (numeric (ch)) lex_numeric (ch, FALSE); else lex_special (ch); /* Return the token type, unless it's a pound sign. ================================================ */ if (token.token != tok_pound_sign) { if (is_debug_on) debug_ptoken(); return token.token; } /* If it is a pound sign, see if it is an include token. ===================================================== */ index = 0; while ((ch = get_char()) && !white_space (ch) && index < max_identifier_size) buffer [index++] = ch; if (index >= max_identifier_size) report_error (err_string_too_long, 0, "get_token"); buffer [index] = '\0'; put_char (ch); if (equal_ignoring_case (buffer, "include")) { token.token = tok_include; if (is_debug_on) debug_ptoken(); return token.token; } output_control_line (buffer); skip_white_space (); } } /*********************************************************************** * * * PUT_TOKEN * * * * This procedure puts a token into the pending token buffer. * * * ***********************************************************************/ void put_token (void) { /* If there is currently a put back token, there's a logic error. */ if (putToken.token != tok_undefined) report_error (err_full_put_token_buffer, 0, "put_token"); /* Save the current token in the put back token structure. */ putToken = token; } /*********************************************************************** * * * CURRENT_TOKEN_STRING * * * * This procedure returns the current token string. * * * ***********************************************************************/ void ptr current_token_string (void) { return (void ptr)token.string; } /*********************************************************************** * * * CURRENT_TOKEN_VALUE * * * * This procedure returns the current token value. * * * ***********************************************************************/ long current_token_value (void) { return token.value; } /************************************************************************** * * * OPEN_SOURCE_FILE * * * * This procedure opens a source file and adds an entry to the Source * * File table. * * * **************************************************************************/ flag open_source_file (uchar ptr src_fname) { HANDLE fnum; /* file handle */ /* Open the source file. */ fnum = CreateFile(src_fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fnum == NULL) { printf ("Unable to open: %s, error %ld\n", src_fname, GetLastError()); close_destination_file (); terminate_input (); exit(1); } /* Initialize a source file structure and insert the entry in the table. */ srcFile = &srcTable [src_entries]; srcFile->fnum = fnum; srcFile->size = GetFileSize(fnum, NULL); srcFile->total_read = 0L; srcFile->buffer = _fmalloc (max_buffer_bytes); src_entries++; /* Read some data into the data buffer. */ return (read_data (srcFile)); } /************************************************************************** * * * INITIALIZE_INPUT * * * * This procedure initializes the input system. * * * **************************************************************************/ void initialize_input (void) { src_entries = 0; srcFile = NULL; } /************************************************************************** * * * TERMINATE_INPUT * * * * This procedure terminates the input system. * * * **************************************************************************/ void terminate_input (void) { SrcFile ptr src; /* source file element */ long i; /* loop counter */ for (i = 0L; i < src_entries; i++) { src = &srcTable [i]; _ffree (src->buffer); } } /*********************************************************************** * * * GET_COMMENT_STRING * * * * This procedure returns the current comment string. * * * ***********************************************************************/ void ptr get_comment_string (void) { if (pszComment == NULL || iComment == 0) return NULL; if (pszComment[0] == char_space) return NULL; return (void ptr) pszComment; } /*********************************************************************** * * * CLEAR_COMMENT_STRING * * * * This procedure clears the comment string. * * * ***********************************************************************/ void clear_comment_string (void) { if (pszComment != NULL) pszComment[0] = 0; iComment = 0; }