Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

A Brief Macro Package for Editing Binary File Systems


SEP91: A BRIEF MACRO PACKAGE FOR EDITING BINARY FILE SYSTEMS

This article contains the following executables: BRIEF.ARC

Jim is a technical support engineer and BRIEF macro enthusiast who has acted as technical advisor to several third-party developers. He can be reached at Solution Systems, 372 Washington Street, Wellesley, MA 02191.


The programmer's editor known as BRIEF (Basic Reconfigurable Interactive Editing Facility) is built on the concept of extensibility and programmability. Macros are so ingrained in the design of BRIEF that many of the standard editing functions are implemented in the macro language, rather than in compiled code. By writing your own BRIEF macros, you can tailor the standard editor in both small ways (such as setting start-up colors, tab settings, or window positions), and large ways (such as modifying existing commands or creating new commands of your own design).

This article describes a package of BRIEF macros that lets you edit binary files in BRIEF, using two side-by-side windows that display both hex and ASCII representations of binary data.

The BRIEF Macro Language CBRIEF, the macro language used here, has a syntax that resembles C. Earlier versions of BRIEF used a LISP-like macro language, which is still supported. In fact, CBRIEF macros are translated to the older LISP syntax before being compiled into a bytecode representation.

The primitives in CBRIEF fall into three categories: language primitives, editing primitives, and DOS primitives. The language primitives provide the usual programming language control structures, operators, and data types. The editing primitives control the core functionality of the text editing engine -- manipulating resources such as windows, buffers, keyboards, cursor position, and other state variables. The DOS primitives let you query the environment for system information, as well run stand-alone DOS programs from within BRIEF.

Text Files vs. Binary Data

The core engine that underlies the standard set of BRIEF macros is designed for editing text files, not binary files. When reading in a file, for example, it converts all zero-valued characters (that is, the ASCII NUL) to spaces. It also expects lines to end with the CR/LF sequence (as found in standard DOS text files), but will also wrap lines when a lone CR is encountered. Editing beyond the end of a screen line or file automatically repositions the terminator character and pads the file accordingly.

A file that contains binary data will therefore obviously conflict with BRIEF's default actions. The default behavior is bound in at a low level -- part of the compiled executable. Unless you want to patch machine-language code, it is hard to modify this behavior directly. Fortunately, BRIEF provides a means of intercepting the default processing of a file by using a "registered" macro. When a macro is registered (with the register_macro primitive), it will be executed every time a particular event occurs. In our case, we will intercept processing whenever a buffer is created by the standard edit_file primitive.

Once this is done, you can spawn an external program to convert the binary file into a corresponding text representation. The user can then edit the resulting text file within BRIEF, and, upon completion, convert it back to its binary form. Actually, our conversion program, BBE, creates two files from the one binary file: a .HEX (hex representation) file and an .ASC file (printable ASCII characters). The hex and ASCII files are displayed in two side-by-side windows and are modified simultaneously by the user. When editing the file, the keyboard bindings are changed to enable newly defined movement keys, 0-F typeable keys, and several function keys.

"Language" Macros

Before going over the listing, some background on "language macros" is necessary. The purpose of language macros is to execute a specific package of language-specific macros associated with a particular file type (a file's type is determined by its file extension, such as .HEX, .C, or .ASM).

All package macros are activated by a registered macro named _call_packages found in LANGUAGE.CB, a standard BRIEF file. This language macro is executed every time a new file is edited or a different buffer is attached to the current window. This mechanism is implemented by creating and maintaining a system buffer database of information specific to file extensions. Whenever a different file extension is encountered, new information is added to the system buffer by parsing the BPACKAGES environment variable.

The _call_packages macro then accesses this information and executes the macros listed for that particular event. A full discussion of this is beyond the scope of this article. For our purposes, what is important is the parsing and execution of the _on event. In the system buffer, we first remove any existing references to .HEX extensions, and then add our package information. The most important of these insertions is the enhancement of the .hex_on line. The macros listed on this line will be called by the language macros every time a .HEX file is attached to the current window.

The name _bin_on was chosen as an example of how _call_package would have created the name if the bin package were being loaded via BPACKAGES. In our case, we modify the system buffer directly, so we can use any arbitrary name. All of this will become more clear as we discuss the implementation.

The Package Implementation

My macro package for editing binary files is shown in Listings One (page 98) and Two (page 100). Note that space restrictions do not allow printing all the source code for the entire package, which amounts to 3000 lines. The complete version is available in electronic form; see "Availability," on page 3.

Whenever any macro file is loaded into BRIEF, the _init macro within the file will be executed. In the case of the binary editing macro package, my_init macro creates a system buffer (__hex _files) which acts as the database for storing the names and true file extensions of files which have been converted. The _init macro also registers the macro (_bin_edit) that intercepts the file editing process and specifies the keyboard assignments used for hex editing. (The use of a leading underscore in a macro name is to prevent it from being called by the user with the F10 key -- the execute_macro command.)

The registered macro _bin_edit recognizes a binary file by the error message "Null characters in file fixed," displayed by BRIEF's edit_file primitive whenever a binary 0 is converted to a space.

After prompting the user for confirmation, _bin_edit calls _hex_edit. Two parameters are passed to _hex_edit: the original filename and a flag. The flag allows a text file to be edited as if it were binary. The system buffer database __hex_files is searched to see if the file is already controlled by the macro package. If not, a command line is created with sprintf, and a DOS program is run to accomplish the file conversion.

Conversion and Reconversion

Writing a program to convert a binary file into formatted hex and ASCII files and back again is not a difficult exercise. The interesting decisions are: how to represent the nondisplayable ASCII characters (NULL, CR, TAB), how to control the program with command-line parameters, and how to format each group of bytes. Parameters to the stand-alone converter, BBE, specify the conversion mode and the names of resulting output files.

BBE accommodates conversions from binary to hex, from Unix to DOS line termination, and from raw data file to fixed-length records. The Unix and fixed-length record features of the package will not be discussed here. The conversion from binary to hex creates two files: an ASCII hex representation with 50 characters per line (representing 25 binary bytes) terminated with a CR-LF sequence and an ASCII representation with the 25 corresponding ASCII text characters.

The reconversion process is handled by write_buffer, a "replacement macro." A replacement macro is a function that can completely replace a standard editor primitive with the same name. More typically, replacement macros are used to alter the default behavior of the original function, by doing a little bit of processing and then calling the original function to complete the processing. This saves having to reimplement complex code from scratch.

A potential problem for the reconversion utility is encountering a byte with a value of 26 (Control-Z, which serves as the DOS end-of-file marker). This is dealt with by ignoring any line whose length is less than 50 characters.

Note that, during the reconversion process, any bytes with the "XX" character sequence are not converted. This is an easy way to implement byte deletion: During interactive editing, when the user presses the delete key, the selected bytes are replaced with Xs in the hex buffer.

Editing Converted Files

After the standalone converter has run, the resulting contents are displayed in two side-by-side windows for interactive editing. The dual-window presentation simplifies the logic for updating the screen display. In the ASCII text window, characters such as CR, TAB, and NULL are represented by a period.

The ASCII buffer is created as a system buffer, in effect hiding it from direct manipulation. The buffer IDs of both buffers, as well as the original filename and extension, are then concatenated into a formatted record to be added to the database. The first two characters in the hex buffer are highlighted to give the visual effect of a two-character byte. If this is the first time the macro has been executed during the edit session, the package information is modified. The removal of existing package information for .HEX files is important because of any default package or equivalencing which may exist. The hex_template_first is used to disassociate the current local keyboard. The _bin_on will then be executed by _call_on_packages due to the modifications. If the flag parameter is set, _call_on_packages may not have been executed, so these are run.

The _bin_on macro accesses the __hex_files database to extract the buffer ID of the system buffer containing the ASCII representation of the converted file. The macro then creates side-by-side windows and attaches the buffers to their respective windows. Each window in BRIEF is associated with a window ID. These values are stored in global variables to allow synchronized updates to the buffers. The _bin_on macro also enables the hex editing keyboard and activates the mouse handler before returning. Because _bin_on creates and changes windows, the language macros must be disabled to avoid recursion during the process of instantiating windows. The registered macros are therefore toggled off using the unregister_macro command.

The Mouse Event Handler

Mouse support is new in version 3.1, so specific discussion of this subject is necessary. BRIEF macros support mouse interaction through mouse events and event handler macros. BRIEF comes supplied with a default mouse event handler, _mouse_action, which can be called from within a mouse macro that you create. My mouse event handler is called _bin_mouse.

Events are passed as parameters to the currently defined mouse macro whenever a mouse event occurs. There are two kinds of events: simple and complex. My _bin_mouse macro uses a separate method to deal with each. Simple events have parameters based on relative position or a static operation. Vertical scroll bar events are a typical example. The parameters passed to the scroll bar events define the position on the scroll bar relative to the thumb button. The processing of simple events in _bin_mouse is done by executing the macro assigned to the corresponding movement key. The inq_assignment primitive is used to obtain the macro name to execute. Mouse macros are associated with a keyboard definition and are only active while that keyboard definition is used.

Complex events receive literal parameters. A "CLICK" event is passed the actual line and column location of the mouse cursor within the buffer where the event occurred. Interpretation of these coordinates is usually required before the desired action is performed. In this example, the parameters are manipulated to position the cursor at the parameter coordinates before the desired action is taken.

Mouse events are passed to the mouse event handler only when they occur in the current window. When an event occurs in a different window, a SET_WIN event is passed to the mouse macro. When this event is interpreted, the _bin_mouse macro uses the set_window primitive to make the window ID parameter the current window. The CLICK event will not be passed to the mouse macro unless the window is changed. Positioning is done from either window by manipulating the column parameter dependent on which window the mouse event occurred in.

Conclusion

Although BRIEF is a full-featured editor, no editor can cover every possible use. Fortunately, the macro language in BRIEF is powerful enough to let you modify even the most intrinsic functions of the standard program.

Products Mentioned

BRIEF, Version 3.1 Solution Systems 372 Washington Street Wellesley, MA 02181 800-677-0001 $249


_A BRIEF MACRO PACKAGE FOR EDITING BINARY FILES_
by James Rodriguez



[LISTING ONE]
<a name="020d_000d">

/* _init _hex_edit _bin_on _bin_off _bin_mouse write_buffer */

#define HEX

#define SHOW_DELAY 50
#define UP        -11
#define DOWN       11
#define LEFT       -1
#define RIGHT       1
#define PGUP      -21
#define PGDN       21
#define TOP        30
#define BOTTOM    -31
#define HOME      -40
#define END       -41

extern   _package_buf,  delete_curr_buffer;

int  __hex_files, __bin_keyboard, __hex_window, __asc_window, __unix_files;
_init ()
{  __hex_files  = create_buffer ("HEXFILES", NULL, 1);  //Binary file database
   __unix_files = create_buffer ("UNIXFILE", NULL, 1);  //Unix file database
   register_macro (6, "_bin_edit");
   keyboard_push ();                   // Create a new keyboard
   set_mouse_action("_bin_mouse");     // Define the mouse handler
   add_hex_keys();                     // Add key assignments
   __bin_keyboard = inq_keyboard ();   // Store the keyboard identifier
   keyboard_pop (1);                   // Reset the stack
}
// _hex_edit: associates a system buffer with a hex buffer and modifies
//            the package information for .HEX files. Called by _bin_edit.
void _hex_edit (string file_name, int in_memory)
{  int   __hex_buffer, __asc_buffer, tmp_buf, current_buffer;
   string   buffer_name, file_ext, buffer_id, file_path;
   global   __hex_buffer, __asc_buffer;

   if (get_parm (0, file_name))
   { get_parm (1, in_memory);   //  Check any flag passed
     if (index (file_name, "."))
     { file_ext  = substr (file_name, 1 + rindex (file_name, "."));
       file_name = substr (file_name, 1,  rindex (file_name, ".") - 1);
     }
     else  file_ext = "";
     buffer_name = substr (file_name, rindex (file_name, "\\") + 1);
     current_buffer = inq_buffer ();
     if (inq_called () == "_bin_edit")
       delete_buffer (current_buffer);
     // Error check for bad buffer name
     tmp_buf = create_buffer (buffer_name + ".hex", file_name + ".hex", 0);
     if (tmp_buf)
     { set_buffer (__hex_files);
       top_of_buffer ();
       if (search_fwd (file_name + ",", 0, 0))
         edit_file (file_name + ".hex");        // It's already in a buffer
       else
       { delete_buffer (tmp_buf);
         sprintf (file_path, "bbe BH %s.%s %s.hex %s.asc>&nul", file_name,
            file_ext, file_name, file_name);
         // Do the hex files already exist?
         if (!exist (file_name + ".hex") && !exist (file_name + ".asc"))
         {  message ("Generating hex file");
            dos (file_path);  // spawn standalone program to convert file
         }
         else
            message ("Editing existing files.");
         __hex_buffer = create_buffer (buffer_name + ".hex", file_name +
            ".hex", 0);
         __asc_buffer = create_buffer (buffer_name + ("." + file_ext),
            file_name + ".asc", 1);
         // Save info in the database for later.
         sprintf (buffer_id, "oldb=%10d,newb=%10d,file=%s,ext=%s",
            __hex_buffer, __asc_buffer, file_name, file_ext);
         set_buffer (__hex_files);
         beginning_of_line ();
         insert (buffer_id + "\n");
         set_buffer (__hex_buffer);
         top_of_buffer ();
         drop_anchor ();
         move_rel (0, 1);
         // Access the package buffer and configure for the hex extension.
         if (first_time ())
         {  if (!_package_buf)   load_macro ("language");
            set_buffer (_package_buf);
            top_of_buffer ();
            while (search_fwd ("<.hex", 1, 0))
              delete_line ();
            // Add the correct lines in packages to make this work.
            insert (".hex_equivalents\n");
            insert (".hex_new;\n");
            insert (".hex_existing;\n");
            insert (".hex_first;\n");
            insert (".hex_on;_on,_bin_on\n"); // Enabled _bin_on
            insert (".hex;=hex,,=hex\n");
            top_of_buffer ();
         }
         set_buffer (__hex_buffer);
         if (! in_memory)
            call_registered_macro (1);
       }
     }
   }
}
// _bin_on:  edits the hex and ascii files in side by side windows by creating
// and moving an edge. Insures correct local keyboard by setting it explicitly.
// Looks in hex_files buffer for filename and extract buffer ids. Associate
// buffers with windows and return.
string _bin_on ()
{  int   i;
   string   buf_to_find,  exten;
   inq_names (buf_to_find, exten);
   buf_to_find = substr (buf_to_find, 1, (rindex (buf_to_find, exten) -
                                                       strlen (exten)) + 1);
   i = inq_buffer();
   set_buffer (__hex_files);  // Edit the system buffer to find buffer id's.
   top_of_buffer ();
   if (search_fwd (buf_to_find + ",", 0, 0))  // Find the line with filename.
   {
     keyboard_flush();                // Remove any pending keystrokes
     use_local_keyboard (0);          // Detach the local keyboard
     keyboard_push (__bin_keyboard);  // Activate the hex keyboard
     set_mouse_action("_bin_mouse");  // Attach the mouse event handler
     beginning_of_line ();
     buf_to_find = trim (read ());    // Parse out the buffer ids
     buf_to_find = ltrim (substr (buf_to_find, index (buf_to_find, "oldb=")
                                                           + 5, 10));
     __hex_buffer = atoi (buf_to_find, 1);
     beginning_of_line ();
     buf_to_find = read ();
     buf_to_find = ltrim (substr (buf_to_find, index (buf_to_find, "newb=")
                                                         + 5, 10));
     __asc_buffer = atoi (buf_to_find, 1);
     set_buffer (__hex_buffer);
     // Unregister the registered macro so a recursive situation does not
     // arise from the window manipulations.
     unregister_macro (1, "_call_on_packages");
     create_edge (3);
     __hex_window = inq_window ();
     move_edge (1, 12);
     change_window (1);
     __asc_window = inq_window ();
     attach_buffer (__asc_buffer);  // Attach the system buffer to a window
     if (!inq_marked ())
          drop_anchor (2);
     else
          refresh ();
     refresh ();
     change_window (3);
     register_macro (1, "_call_on_packages"); // Enable the language macro.
     returns "_bin_off"; // Return the off event for the language package.
     }
   else
   { set_buffer(i); // This is an error check to allow editing
     returns "";    // of files with .hex extensions which are not
   }                // under the binary package control.
}
// _bin_off --  deletes the created window and resets the keyboard.
void _bin_off ()
{  keyboard_flush();
   keyboard_pop ();
   keyboard_push ();
   add_hex_keys();
   __bin_keyboard = inq_keyboard ();
   delete_edge (1);   // Delete the window
   keyboard_pop (1);
   set_mouse_action("_mouse_action"); // Reset the mouse handler
}
#define BUTTON_1_CLICK     10
#define BUTTON_2_CLICK     11
#define BUTTON_1_DBLCLK    13
#define BUTTON_2_DBLCLK    14
#define VERTICAL_SCROLL    17
#define CLOSE_WINDOW       19
#define SET_WINDOW         20
#define STATUS_AREA        21
#define SCROLLBAR__LINEUP   0
#define SCROLLBAR__LINEDOWN 1
#define SCROLLBAR__PAGEUP   2
#define SCROLLBAR__PAGEDOWN 3
#define SCROLLBAR__TOP      6
#define SCROLLBAR__BOTTOM   7
#define TITLE_BAR           1

void _bin_mouse(int action, int modifier, int line, int col)
{ if (inq_window() == __asc_window)
       col = col*2; // Modify the column parameter if clicked in ascii window
   switch (action)
   {
     case STATUS_AREA:   // Go to offset on status area click
       execute_macro(inq_assignment("<Alt-g>"));
     case SET_WINDOW:     // A different window was selected
     {
       if (col!=TITLE_BAR && line == __asc_window)  // Disregard mouse action
       {                                            // on the border.
         unregister_macro (1, "_call_on_packages");
         set_window (__asc_window);
       }
     }
     case BUTTON_2_CLICK:
     case BUTTON_2_DBLCLK:
     case BUTTON_1_CLICK:
     case BUTTON_1_DBLCLK:
     { int lines,cols;
       if (col % 2)    // Modify column parameter to event byte value
         col++;
       if (col > 50)   // If past formatted string length go to the end of
         col = 50;     // the string.
       unregister_macro (1, "_call_on_packages");
       set_window (__hex_window);
       inq_position(lines, cols);
       raise_anchor ();
       move_rel(0,-1);
       save_position();
       if (move_abs(line,col))
       {
         // If beyond the end of buffer ignore the action
         if (! inq_position(lines,cols) && lines==line && cols == col)
         {  restore_position(0);
            move_rel(0,-1);
            set_window (__asc_window);
            raise_anchor ();
            move_abs(line,col / 2);
            drop_anchor (2);
            refresh ();
            set_window (__hex_window);
         }
         else
            restore_position();
       }
       else
          restore_position();
       drop_anchor ();
       move_rel (0, 1);
       register_macro (1, "_call_on_packages");
       refresh ();
       switch (action)
       {
         case BUTTON_2_DBLCLK:  // Opens a line (feature not shown)
         {  execute_macro(inq_assignment("<Ctrl-Enter>"));
         }
         case BUTTON_1_DBLCLK: // Double click modifies current byte.
         {  string sread, character;
            int hex_val;
            raise_anchor ();
            move_rel (0, -1);
            sread = "Enter new value for ";
            if (read (1) != "\n")
              sread += read (2);
            sread += ": ";
            if (get_parm (NULL, character, sread, 2))
            { hex_val = _bin_atoh (character);
              sprintf (character, "%02x", hex_val); // Convert int to hex
              unregister_macro (1, "_call_on_packages");
              set_window (__asc_window);
              switch (hex_val) // Make sure the value is displayable.
              { case 13:
                case 9:
                case 0:
                    sread = ".";
                default:
                    sprintf (sread, "%c", hex_val);
                }
              /* Insert the value in the hex and ascii buffers
              ** rehighlight both windows and return. */
              insert ("%s", sread);
              raise_anchor ();
              delete_char ();
              move_rel (0, -1);
              drop_anchor (2);
              refresh ();
              set_window (__hex_window);
              insert ("%s", upper (character));
              delete_char (2);

              move_rel (0, -2);
            }
            drop_anchor ();
            move_rel (0, 1);
            refresh ();
            register_macro (1, "_call_on_packages");
            refresh ();
         }
       }
     }
     case VERTICAL_SCROLL:  // Vertical scroll bar events.
     {
       switch (line)
       {
         case SCROLLBAR__LINEUP:               // Click on up arrow
            execute_macro(inq_assignment("<Left>"));
         case SCROLLBAR__LINEDOWN:             // Click on down arrow
            execute_macro(inq_assignment("<Right>"));
         case SCROLLBAR__PAGEUP:               // Click above thumb button
            execute_macro(inq_assignment("<PgUp>"));
         case SCROLLBAR__PAGEDOWN:             // Click below thumb button
            execute_macro(inq_assignment("<PgDn>"));
         case SCROLLBAR__TOP:               // Double click on up arrow
            execute_macro(inq_assignment("<Ctrl-PgUp>"));
         case SCROLLBAR__BOTTOM:               // Double click on down arrow
            execute_macro(inq_assignment("<Ctrl-PgDn>"));
       }
     }
   }
}
// write_buffer:  A replacement for write_buffer. Checks file extension, cleans
// up system buffers. Note: conversion back to binary is not done if buffer
// has not been modified.
replacement int write_buffer ()
{
   int   buf_to_edit, buf_to_delete, file_is_controlled, file_was_modified;
   string   file_name, ext, response, response2, old_buffer, write_command;
   inq_names (file_name, ext);
   /* if write_buffer was not called from the keyboard and the extension
   ** isn't .hex or .unx call write_buffer. */
   if ("" == inq_called () && (ext == "hex" || ext == "unx"))
     if (get_parm (0, response, "Convert file? ", 1, "Y"))
       if (get_parm (1, response2, "Delete buffer? ", 1, "Y"))
       { int   file_is_hex;
         file_is_hex = 0;
         buf_to_edit = inq_buffer (); // Store the current buffer id
         raise_anchor ();         // Remove the highlight
         if (inq_modified ())  // Only write and convert if changed
         {
            returns write_buffer ();  // Preserve the return value
            file_was_modified = 1;
         }
         else
            file_was_modified = 0; // if not changed don't reconvert
         // Remove the extension.
         if (index (file_name, "."))
            file_name = substr (file_name, 1, rindex (file_name, ".") - 1);
         // Make the system buffer database current
         if (ext == "hex")
         {
            set_buffer (__hex_files);
            file_is_hex = 1;
         }
         else
            set_buffer (__unix_files); // Not used in this example
         top_of_buffer ();
         // Try to find filename record and extract original extension.
         if (search_fwd (file_name + ",ext=", 0, 0))
         {  beginning_of_line ();
            ext = trim (read ());
            file_is_controlled=1;
            // Find the ascii buffer associated with the hex buffer
            if (file_is_hex)
            { int temp_buffer=inq_buffer();
              ext = ltrim (substr (ext, index (ext, "newb=") + 5));
              old_buffer = substr (ext, 1, 10);
              // Retrieve the buffer id
              buf_to_delete = atoi (old_buffer, 1);
              set_buffer(buf_to_delete);
              raise_anchor();
              write_buffer();
              drop_anchor(2);
              set_buffer(temp_buffer);
              if (upper (response2) == "Y")
                delete_buffer (buf_to_delete);
            }
            ext = trim (substr (ext, rindex (ext, "=") + 1));
            beginning_of_line ();
            if (upper (response2) == "Y")
              delete_line (); // Delete the record
         }
         // Reset the current buffer
         set_buffer (buf_to_edit);
         if (file_is_hex)
            sprintf (write_command, "bbe HB %s.hex %s.%s>&nul", file_name,
              file_name, ext);
         else
            sprintf (write_command, "bbe DU %s.unx %s.%s>&nul", file_name,
              file_name, ext);
         if (file_was_modified && upper (response) == "Y" &&
            file_is_controlled)
         {
            message ("%s", write_command);
            dos (write_command);        // Call DOS to execute the conversion
            message ("Conversion complete");
         }
         // Delete the current buffer and the converted file(s).
         if (upper (response2) == "Y")
         {
            if (1 != delete_curr_buffer())
            {
              file_is_controlled=-1;
              delete_buffer(buf_to_edit);
            }
            if (file_is_hex)
            {
              sprintf (response, "%s.hex", file_name);
              del (response);
              sprintf (response, "%s.asc", file_name);
            }
            else
              sprintf (response, "%s.unx", file_name);
            del (response);
            if (file_is_controlled == -1)  // No other buffers
              exit("y");
         }
         else
            if (file_is_hex && file_is_controlled)
            {
              move_rel (0, -1);
              drop_anchor ();
              move_rel (0, 1);
            }
         }
       else ;
     else ;
   else // Call the primitive
     return write_buffer ();
}



<a name="020d_000e">
<a name="020d_000f">
[LISTING TWO]
<a name="020d_000f">

/* _bin_add _bin_atoh add_hex_keys _bin_edit _bin_delete */

// _bin_move -- this macro handles synchronized positioning.
void _bin_move (int direction)
{  int   col, line;
   get_parm(0,direction);
   raise_anchor ();
   unregister_macro (1, "_call_on_packages");  // Remove the language control
   set_window (__asc_window);
   raise_anchor ();
   inq_position (line, col);
   switch (direction)
   { case LEFT:
     case RIGHT:
     {
       if ((col == 25 && direction == RIGHT) || (col==1 && direction==LEFT))
       {  // Scrolling is needed
         move_rel (direction,0);
         col = (direction == LEFT ? 25 : 1);
         move_abs (0,col);
       }
       else
         move_rel (0,direction);
     }
     case UP:
     case DOWN:
       move_rel (direction % 10,0);
     case PGUP:
     case PGDN:
     { inq_window_size(line);  // Get the number of lines in the window
       move_rel ((direction % 20) * line,0);
     }
     case HOME:
       move_abs(0,1); // Beginning of line
     case END:
       move_abs(0,25); // End of line
     case TOP:
         move_abs(1,1); // Top of buffer
     case BOTTOM:
     { end_of_buffer();
       move_abs(0,25);  // End of buffer
     }
   }
   while (inq_position (line,col))  // We might be in virtual space if
     move_rel (-1, 0);           // so move up until we're not.
   drop_anchor (2);
   refresh ();
   set_window (__hex_window);
   move_abs(line,col * 2);   // Reposition in the hex buffer
   move_rel (0, -1);     // Highlight the current byte.
   drop_anchor ();
   move_rel (0, 1);
   refresh ();
   register_macro (1, "_call_on_packages");
}
// _bin_add: overwrites the byte in hex window. It is assigned to all of valid
//  hex editing keys and uses push_back to get the original key.
void _bin_add (string key_read)
{  string   character,  sread;
   int      hex_val, temp_hex;
   // Get the parameter which is key pressed and push it back on keyboard
   // buffer so that it displays in prompt as if it were typed there.
   get_parm (0, key_read);
   push_back (key_to_int (key_read));
   raise_anchor ();
   // Read the text from the buffer to display the old value at the prompt.
   move_rel (0, -1);
   sread = "Enter new value for ";
   if (read (1) != "\n")
     sread += read (2);
   sread += ": ";
   if (get_parm (1, character, sread, 2))
   {                            // Limit the prompt response to two characters
     hex_val = _bin_atoh(character);
     sprintf (character, "%02x", hex_val); // Convert the int to hex.
     unregister_macro (1, "_call_on_packages");  // Don't disturb the windows.
     set_window (__asc_window);
     switch (hex_val)  // Make sure the value typed is displayable
     { case 13:
       case 9:
       case 0:
         sread = ".";
       default:
         sprintf (sread, "%c", hex_val);
     }
     raise_anchor ();
     delete_char ();  // Remove the old character and insert the new.
     insert ("%s", sread);
     move_rel(0,-1);
     drop_anchor (2);
     refresh ();
     set_window (__hex_window);
     delete_char (2); // Remove the old byte and insert the new.
     insert ("%s", upper (character));
     }
   drop_anchor ();
   move_rel (0, 1);
   refresh ();
   register_macro (1, "_call_on_packages"); // Reenable the language macro
   refresh ();
   execute_macro(inq_assignment("<Right>")); // Move to the next byte.
}
// _bin_delete: converts the current byte to XX which will be ignored when
// reconverted. Parameter is for deleting a full line.
void _bin_delete (~int line)
{  get_parm(0,line);
   unregister_macro (1, "_call_on_packages");
   set_window (__asc_window);
   raise_anchor ();
   if (line)
   { save_position();
     move_abs(0,1);
     drop_anchor(3);
     translate("?",".",1,1,1,1);  // Change any character to a "."
     raise_anchor ();
     restore_position();
   }
   else
   { delete_char ();
     insert (".");
     move_rel (0, -1);
   }
   drop_anchor (2);
   refresh ();
   set_window (__hex_window);
   raise_anchor ();
   if (line)
   { save_position();
     move_abs(0,1);
     drop_anchor(3);
     translate("?","X",1,1,1,1);  // Change any character to "X"
     raise_anchor ();
     restore_position();
     move_rel (0, -1);
   }
   else
   { move_rel (0, 1);
     insert ("XX");
     move_rel (0, -4);
     delete_char (2);
   }
   drop_anchor ();
   move_rel (0, 1);
   register_macro (1, "_call_on_packages");
   refresh ();
}
// _bin_atoh --  accepts a "hex" string and returns an integer.
int _bin_atoh (string to_convert)
{  string   str_rev;
   int      converted, loop_count,  t_int;

   get_parm (0, to_convert);

   // Read a character from the end of the string and multiply
   // it by the loop count which is multiplied by 16 on each iteration.
   while (strlen (trim (to_convert)))
   { str_rev = substr (to_convert, strlen (to_convert), 1);
     t_int = index("123456789ABCDEF",upper(str_rev));
     loop_count *= 16;
     if (0 == loop_count)
       loop_count = 1;
     converted += t_int * loop_count;
     to_convert = substr (to_convert, 1, strlen (to_convert) - 1);
   }
   return converted;
}
// add_hex_keys -- adds hex editing keys to the keyboard.
add_hex_keys()
{  string macro_name, key_name;
   int loop;
   for (loop=48; loop<58; loop++)   // Assign the numeric keys
   {                              // to the keyboard
     sprintf (key_name, "<%c>",loop);
     sprintf (macro_name, "_bin_add \"%c\"",loop);
     assign_to_key (key_name, macro_name);
   }
   for (loop=65;loop<71;loop++)  // Assign the a-f Hex keys
   {                             // to the keyboard
     sprintf (key_name, "<%c>",loop);
     sprintf (macro_name, "_bin_add \"%c\"",loop);
     assign_to_key (key_name, macro_name);
     assign_to_key (lower(key_name), macro_name);
   }
   assign_to_key ("<Alt-n>", "edit_next_buffer");
   assign_to_key ("<Alt-e>", "edit_file");
   assign_to_key ("<Alt-w>", "write_buffer");
   assign_to_key ("<Alt-x>", "exit");
   sprintf (macro_name, "_bin_move %d", UP);
   assign_to_key ("<Up>", macro_name);
   sprintf (macro_name, "_bin_move %d", DOWN);
   assign_to_key ("<Keypad-Enter>", macro_name);
   assign_to_key ("<Down>", macro_name);
   sprintf (macro_name, "_bin_move %d", LEFT);
   assign_to_key ("<Left>", macro_name);
   sprintf (macro_name, "_bin_move %d", RIGHT);
   assign_to_key ("<Right>", macro_name);
   sprintf (macro_name, "_bin_move %d", PGUP);
   assign_to_key ("<PgUp>", macro_name);
   sprintf (macro_name, "_bin_move %d", PGDN);
   assign_to_key ("<PgDn>", macro_name);
   sprintf (macro_name, "_bin_move %d", TOP);
   assign_to_key ("<Ctrl-Pgup>", macro_name);
   sprintf (macro_name, "_bin_move %d", BOTTOM);
   assign_to_key ("<Ctrl-PgDn>", macro_name);
   sprintf (macro_name, "_bin_move %d", HOME);
   assign_to_key ("<Home>", macro_name);
   sprintf (macro_name, "_bin_move %d", END);
   assign_to_key ("<End>", macro_name);
}
// _bin_edit -- registered macro for trapping Null files message.
void _bin_edit ()
{  if (inq_message () == "Null characters in file fixed.")
   { string   okay;
     string   file;
     inq_names (file);
     // See if the conversion is requested.
     if (get_parm (0, okay, "Okay to create HEX file to edit? ", 1, "Y"))
       if (upper (okay) == "Y")
         _hex_edit (file, 0); // Call _hex_edit to do conversion
       else // Other wise let the user frustrate themselves.
         message ("You asked for it.");
   }
}
on_packages");  // Don't disturb the windows.
     set_window (__asc_window);


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.