McIDAS Programmer's Manual
Version 2003

[Search Manual] [Table of Contents] [Go to Previous] [Go to Next]


McIDAS developer overview

The previous two sections introduced you to McIDAS-X from a user's perspective and provided an overview of the McIDAS-X environment. This final section describes how to build applications that will interact with the McIDAS-X environment. In this section, you will learn how to:

This chapter contains references to specific source files. The table below describes the suffixes McIDAS-X uses to name source files.

Suffix Language Description

.c

C

functions and McIDAS-X commands

.cp

C

ADDE servers and non-McIDAS-X applications

.dlm

Fortran

dynamic link modules

.for

Fortran

functions and subroutines

.fp

Fortran

ADDE servers and non-McIDAS-X applications

.h

C

include files

.inc

Fortran

include files

.mac

Fortran

McIDAS-X macros

.pgm

Fortran

McIDAS-X commands

Writing McIDAS-X applications

You can write McIDAS-X applications in either C or Fortran. This section describes the concepts you need to know to write basic McIDAS-X applications in either language, along with the formats and policies for writing online command helps and setting status codes.

Fortran programs

When you write a Fortran program for McIDAS-X, you can't write it as a Fortran MAIN program. Instead, you must place it in the McIDAS-X environment as a subroutine named MAIN0, which is linked with a command jacket called main.

The command jacket main initializes the run-time environment for the command, including:

The first executable line of any McIDAS-X application written in Fortran must be the statement subroutine main0. The code fragment below is a sample command to print the phrase Hello World to the McIDAS-X Text and Command Window.


subroutine main0 
call sdest ('Hello World',0)
call mccodeset (0)
return
end

C programs

When writing a C program for McIDAS-X, the main is provided directly in the C code. The first thing you will do when writing a McIDAS-X application in C is to initialize the McIDAS-X environment with a call to the function Mcinit. This function performs the same command environment initialization performed by the Fortran command jacket main. To write McIDAS-X applications in C, your source code should also contain a reference to the McIDAS-X include file mcidas.h.

The code fragment below demonstrates how the same McIDAS-X command to print Hello World is written as a C application.

#include <stdio.h>
#include "mcidas.h"

int main (int argc, char **argv)
{

  /* initialize the McIDAS environment */

  if (Mcinit
 (argc, argv) < 0)
  {
    fprintf  (stderr, "%s\n", Mciniterr ());
    return (1);
  }

  Mcprintf ("Hello World\n");

  Mccodeset (0);
  return (Mccodeget());
}

Include files

McIDAS-X uses include files to hold definitions of constants specific to its software. mcidas.h contains system-wide constants and function prototypes. Equivalanet constants for Fortran applications are found in the *.inc files.

mcidas.h

The include file mcidas.h contains all the function prototypes for C-callable API routines in the McIDAS-X library. It also contains two constant values that are commonly used in McIDAS-X applications.

Constant value
Description

MCMISSING

1-byte McIDAS-X standard missing value code

MCMISSING4

4-byte McIDAS-X standard missing value code

mcidas.h also includes a group of defined types that you should use to ensure platform independence of data types. Use the defined types below when writing interface routines that will be used between C and Fortran.

Defined type
Description

Fdouble

equivalent to a double precision declaration type in Fortran

Fint

equivalent to an integer declaration type in Fortran

Fint2

equivalent to an integer*2 declaration type in Fortran

Fint4

equivalent to an integer*4 declaration type in Fortran

Freal

equivalent to a real declaration type in Fortran

Freal4

equivalent to a real*4 declaration type in Fortran

Freal8

equivalent to a real*8 declaration type in Fortran

FsLen

used for passing string lengths into Fortran routines

Mcint2

2-byte signed integer

Mcuint2

2-byte unsigned integer

Mcint4

4-byte signed integer

Mcuint4

4-byte unsigned integer

To ensure portability of Fortran jackets written in C, the preferred types to use are Fdouble, Fint and Freal, as the INTEGER*2, INTEGER*4, REAL*4 and REAL*8 declarations are not part of the Fortran-77 standard.

Error handling

In both Hello World examples above, calls were made to the functions mccodeset and Mccodeset. These functions set a status code that can be passed from the application to the calling environment. Currently, the acceptable return codes for McIDAS-X commands are as follows:

Return code
Description

0

command completed successfully

1

command contained an unrecoverable error

2

command contained an error that may be recoverable

3 - 99

reserved for SSEC

100 - 127

site-defined return values

This information may be very useful to the calling environment, especially when using McIDAS-X commands in scripts designed for batch processing. For example, if you have a script that copies satellite images from several different sources and then creates a mosaic of the images, you may not want the script to generate the mosaic until all of the images are available. The processing script will not be able to verify that all of the data is available until all of the commands necessary to acquire the satellite data have returned a status of 0 indicating success.

If a command returns a status of 1, it typically means the user entered the command incorrectly and it will never run properly. A status of 2 is typically returned when data requested by a user is not yet available.

Setting return statuses for McIDAS-X applications became mandatory, beginning with McIDAS-X version 7.0 released in May 1996. Most McIDAS-X applications written before that upgrade do not yet contain the Mccodeset /mccodeset features.

Online command helps

The McIDAS-X command provides users with a quick way to get a listing of a command's structure and available options. The original text for these helps is stored in the source file for each command. You will use one of two template formats when creating online helps. The format for Fortran commands is shown below.

C ? NAME -- Describe the purpose of this command
C ?    NAME FUNCT1 parm1 parm2 <keywords> "quote
C ?    NAME FUNCT2 parm1 <keywords>
C ? Parameters:
C ?    FUNCT1   | describe the purpose of this function option
C ?    FUNCT2   | describe the purpose of this function option
C ?    parm1    | describe this parameter (def=default value)
C ?    parm2    | describe this parameter (def=default value)
C ?    "quote   | describe the contents of the quote string
C ? Keywords:
C ?    KEYNAME= | describe values (def=default values)
C ?    KEY2=YES | describe effect (def=default value)
C ? Remarks:
C ?    Add remarks, from most to least important. Use complete
C ? sentences. Separate multiple remarks with a single blank line, 
C ? as below.
C ?
C ?    Always end the help section with a line of 10 dashes,
C ? as below.
C ? ----------

The similar help template for commands written in C is shown below.

/*
*?  NAME -- Describe the purpose of this command
*?     NAME FUNCT1 parm1 parm2 <keywords> "quote
*?     NAME FUNCT2 parm1 <keywords>
*?  Parameters:
*?     FUNCT1   | describe the purpose of this function option
*?     FUNCT2   | describe the purpose of this function option
*?     parm1    | describe this parameter (def=default value)
*?     parm2    | describe this parameter (def=default value)
*?     "quote   | describe the contents of the quote string
*?  Keywords:
*?     KEYNAME= | describe values (def=default values)
*?     KEY2=YES | describe effect (def=default value)
*?  Remarks:
*?     Add remarks, from most to least important. Use complete
*?  sentences. Separate multiple remarks with a single blank line, 
*?  as below.
*? 
*?     Always end the help section with a line of 10 dashes,
*?  as below.
*?  ----------
*/

The first line of each help contains the command name followed by two dashes (--). These dashes are used as flags to the system for the Alt ? help option described earlier in this chapter.

 

To learn how to build help files, see Chapter 3, Getting Started in McIDAS-X.

The McIDAS-X library

In the Hello World examples above, several function names are in bold type. These are examples of functions included in the McIDAS-X library. The library contains all the object code for the functions and subroutines that make up the McIDAS-X Application Program Interface (API).

The name of the McIDAS-X library is libmcidas.a.

API naming conventions

To provide easy recognition of McIDAS-X functions, the names of all new McIDAS-X functions begin with one of the prefixes below.

Prefix
Description

Mc

C-callable, API-level

mc

Fortran-callable, API-level

M0

C-callable, non-API

m0

Fortran-callable, non-API

The McIDAS-X philosophy regarding function prefixes is that once a function is released with an Mc or mc prefix, it is a stable member of the McIDAS-X library and will have neither its functionality nor calling sequence changed. The M0 or m0 prefix is assigned to:

SSEC uses the M0/m0 prefix while testing a routine's functionality and calling sequence. When the function is deemed stable, the prefix is changed to Mc/mc.

SSEC adopted this function prefix naming convention in the spring of 1995. Most of the routines currently found in the McIDAS-X library predate this naming convention. As SSEC modifies existing software, it will make every effort to change the names of older functions where practical.

If new functions are written to replace older, non-prefixed functions, SSEC's policy for removing the old functions from McIDAS-X is as follows:

Interface documentation block

SSEC has also developed a standard interface documentation block template that you should use for all new McIDAS-X library functions. This format is used to generate the online API function documentation distributed with the McIDAS-X software.

The table below lists and defines each field in the documentation block. More information follows along with examples from the mchmstostr function. If a function doesn't require a description for every field, you should still include all fields in the documentation block and fill the unused fields with the word none.

Field
Description

Name

name of the function

Interface

details of the function's calling sequence and return type

Input

variables in the calling sequence that are input only

Input/Output

variables in the calling sequence used for both input and output

Output

variables in the calling sequence that are output only

Return values

possible function return values and what they mean

Remarks

information about a function that may be useful to a programmer

Categories

keywords identifying which subsystem this function operates on

Name

This field is usually a one-line description of what the function does. The example below is taken from the function mchmstostr.

Name:
    mchmstostr - Converts a time to a character string

Interface

This field describes the function type value returned, such as float, integer, or subroutine. It also contains any include files associated with the function, and the type and size of each parameter in the calling sequence.

Interface:
    integer function
    mchmstostr (integer hms, integer format, character*(*) string)

Input

This field describes each of the input parameters to a function. It should include any discussion of expected formats and units.

Input:
    hms - Time in the form hhmmss.
    format  - Output format desired for the string.

Input and Output

This field describes parameters that pass input to a function and have their values modified within the function. It is important to describe the state of these parameters both on input and output. Since this example doesn't contain parameters in this field, the entry looks like this:

Input and Output:
    none

Output

This field describes each of the output parameters to a function. The description should include any discussion of expected formats and units.

Output:
    string  - Destination character string.

Return values

This field describes the possible values that can be returned by this function. The McIDAS-X convention for return values is described below.

  • If a function has only one successful return value, it should be 0.
  • If a function has multiple successful return values, it should return positive numbers.
  • If a function fails, it should return negative numbers.

A function should have as many unique failure return values as ways the function can fail, as shown in the example below.

Return values:
     0  - Success.
    -1  - Invalid value for hms.
    -2  - Invalid value for format.
    -4  - Destination string is not big enough.

Remarks

This field describes additional information that is necessary or useful to a programmer. For example, it would be helpful for a programmer to know:

  • If this function allocates memory, requiring the memory to be freed when it's no longer used
  • If this function must call another function to initialize the environment before it can be used
  • If this function has size limits or infinities

Other useful information may include references to:

  • A book where an algorithm used in a function was found
  • Other functions in the library having similar features

An example of this field for mchmstostr is shown below.

Remarks:
    If the input value for hms is 23444 then:
        form    string
        1   02:34:44Z
        2   02:34:44
        3   02:34:44UTC
        4   02:34:44 Z
        5   02:34:44 UTC
        6   2:34:44

Categories

This field is used to generate the cross reference list for the online API function documentation. The categories available in McIDAS-X are:

calibration converter day/time display
event file graphic grid
image ingest/decode met/science navigation
point text sys_config system
user_interface utility

 

 


Be careful not to assign more categories than needed to a function. For example, the McIDAS-X library function to display a contour graphic of a grid is mcgrdcon. The logical categories for this routine are graphic and grid. The function mcgget retrieves a grid, so the logical category for this routine is grid. Even if that grid is stored in a file, you wouldn't need to assign the category file since the fact that the grid is stored in a file is irrelevant. Use the utility category sparingly; it is intended only for functions that do not easily fit into any other category.

In mchmstostr, the categories field looks like this:

Categories:
    converter
    day/time

The next two pages contain the complete interface documentation block templates for both C and Fortran functions. Although the categories field contains the complete list of available entries, you will choose only those appropriate to your function. Text files containing the templates are available on the MUG Web Site.

Fortran template

C   THIS IS SSEC PROPRIETARY SOFTWARE - ITS USE IS RESTRICTED.

C *** McIDAS Revision History ***
C *** McIDAS Revision History ***

*$ Name:
*$      mcname - short description of purpose/use/etc
*$
*$ Interface:
*$      subroutine
*$      integer function
*$      double precision function
*$      mcname(integer param1, character*(*) param2, integer param3(64))
*$
*$ Input:
*$      none
*$      param1  - description of it
*$
*$ Input and Output:
*$      none
*$      param2  - description of it
*$
*$ Output:
*$      none
*$      param3  - description of it
*$
*$ Return values:
*$       0      - success
*$
*$ Remarks:
*$      Important use info, algorithm, etc.
*$
*$ Categories: 
*$      grid 
*$      image 
*$      point 
*$      text 
*$      system 
*$      event 
*$      file 
*$      sys_config 
*$      display 
*$      graphic 
*$      utility 
*$      converter 
*$      day/time 
*$      calibration 
*$      navigation  
*$      ingest/decode 
*$      met/science 
*$      user_interface

        INTEGER FUNCTION MCNAME (...)
        IMPLICIT NONE
C --- symbolic constants & shared data
      (...)
C --- external functions
      (...)
C --- local variables
      (...)
C --- initialized variables
      (...)

C template

/* THIS IS SSEC PROPRIETARY SOFTWARE - ITS USE IS RESTRICTED. */

/**** McIDAS Revision History *** */
/**** McIDAS Revision History *** */

#include "mcidas.h"

/*
*$ Name:
*$      Mcname - short description of purpose/use/etc
*$
*$ Interface:
*$      #include "mcidas.h"
*$
*$      int
*$      Mcname(int param1, char *param2, int *param3)
*$
*$ Input:
*$      none
*$      param1  - description of it
*$
*$ Input and Output:
*$      none
*$      param2  - description of it
*$
*$ Output:
*$      none
*$      param3  - description of it
*$
*$ Return values:
*$       0      - success
*$
*$ Remarks:
*$      Important use info, algorithm, etc.
*$
*$ Categories: 
*$      grid 
*$      image 
*$      point 
*$      text 
*$      system 
*$      event 
*$      file 
*$      sys_config 
*$      display 
*$      graphic 
*$      utility 
*$      converter 
*$      day/time 
*$      calibration 
*$      navigation  
*$      ingest/decode 
*$      met/science 
*$      user_interface
*/

int
Mcname(...)

C and Fortran function interfaces

McIDAS-X applications and functions can be written in either Fortran or C. Because McIDAS has its roots in Fortran, not all functions in the McIDAS-X library have both C and Fortran calling interfaces.

While McIDAS-X attempts to accommodate both languages, C language programs will sometimes need to interface with Fortran-coded functions from the McIDAS-X library. When calling Fortran functions from C applications, you must keep in mind these four important differences between the languages.

Below are examples of how these language differences affect a C application calling a Fortran function. Also provided is an example of writing Fortran-callable routines in C and information about writing Dynamic Link Library modules.

Passing parameters into functions

The McIDAS-X Fortran function iyxll converts integer values from frame (tv) line and elements to single precision floating point values for latitude and longitude. The sample Fortran code fragment below calls this function.

integer     tvlin, tvele
integer onscreen
real    lat, lon

tvlin   = 100
tvele   = 200

onscreen    = iyxll(tvlin, tvele, lat, lon)

To call the same function from a C application, your code would be similar to this:

#include "mcidas.h"

Fint iyxll_ (Fint *, Fint *, Freal *, Freal *); /* function prototype */

Fint    tvlin, tvele;
Freal   lat, lon;
Fint    onscreen;

tvlin   = 100;
tvele   = 200;

onscreen= iyxll_ (& tvlin, & tvele, & lat, & lon);

The special type declarations Fint and Freal are Fortran integer and real declarations defined in the include file mcidas.h. They are designed to provide platform-independent interfaces between Fortran and C applications.

Since parameters are passed into functions in C by value, you must reference the variables in the calling sequence with the ampersand (&) symbol. Calling iyxll by value, as shown below, would most likely result in a segmentation violation error.

onscreen    = iyxll_ (tvlin, tvele, lat, lon);

Passing character strings into functions

The McIDAS-X library function mdsvc, which is coded in Fortran, returns the appropriate real-time MD file given a character string schema type and an integer value representing a Julian day. Below is a sample Fortran code fragment to call this function.

integer mdfile, day
character*4 schema

schema  = 'ISFC'
day = 96017

mdfile = mdsvc(schema, day)

To call the same function from a C application, your code would be similar to this:

#include "mcidas.h"

Fint mdsvc_ (char *, Fint *, FsLen); /* function prototype */

Fint    mdfile, day;
FsLen   schema_length;
char    *schema;

day           = 96017
schema        = strdup ("ISFC");
schema_length = (FsLen) strlen (schema);

mdfile = mdsvc_ (schema, &day, schema_length);

Note that the variable schema is passed in with no ampersand character. The function expects the variable to be passed in by address and since schema is already a pointer, no ampersand is required.

Also note that the call to mdsvc_ in C has an additional parameter, schema_length. This argument is passed by value to the Fortran function so the length of the string containing the schema type is known to mdsvc internally.

Storing arrays

The conflict of Fortran column-major arrays and C row-major arrays is difficult to resolve. Arrays are contiguous memory segments, no matter how many dimensions they have. The difference between the languages is in how the individual elements of the arrays are arranged and accessed, as described below.

If you declare a Fortran array like this:

integer array(2,3)

Then to reference the memory segments in increasing order, you will reference them like this:

1,1  2,1  1,2  2,2  1,3  2,3

If you declare a C array with the same dimensions:

int array[2][3]

Then to reference the memory segments in increasing order, you will reference them as shown below. They are shown as one-based values for comparison to the Fortran array even though the C language is actually zero-based.

1,1  1,2  1,3  2,1  2,2  2,3

When accessing data stored in multi-dimensional arrays, it is difficult to recognize if an array is stored in Fortran or C. Since most McIDAS-X applications were historically written in Fortran, multi-dimensional data is usually stored in column-major format. To address values for column-major data in C, dimension your arrays as one-dimensional and use the conversion equations below to index to the appropriate location.

The indexing equation for a column-major (Fortran) array is as follows, assuming 1-based indexing:

index = ((column-1)*Number Of Rows) + row

The indexing equation for a row-major (C) array is as follows, assuming 0-based indexing:

index = (row*Number Of Columns) + column

Writing Fortran-callable routines in C

Now that you understand how the C and Fortran languages treat different elements of parameter passing into functions, you need to know how to write Fortran-callable functions in the C language. The example below builds the calling sequence for a unit conversion routine that takes an integer value and unit types and returns a floating point number. The function also returns an integer type for a status code.

In C, the prototype and function would look like this:

int convert (int, char *, char *, float *);   /* function prototype */

int convert (int input, char *input_units, 
             char *output_units, float *output)
{
  :
  :
}

The entire source code for the Fortran-callable version of this function would appear as follows. The important concepts are bolded.

Fint convert_ (Fint * input,
               char  * input_units,
               char  * output_units,
               Freal * output, 
               FsLen input_len,
               FsLen output_len)
{
  char    *c_input_units;        /* C string representation
                                  * of input units */
  char    *c_output_units;       /* C string representation
                                  * of output units */
  int      rc;                   /* convert() function return value */

  /* convert the Fortran character strings into C strings */

  c_input_units  = fsalloc (input_units, input_len);
  c_output_units = fsalloc (output_units, output_len);

  /* call the C callable version of convert */

  rc = convert ((int) * input, c_input_units, c_output_units, 
                (float *) output);

  /* free up the memory for the C strings */

  free (c_input_units);
  free (c_output_units);

  /* return from the function */

  return ((Fint) rc);
}

Note that the Fortran-callable function declaration explicitly includes the underscore character (_). The McIDAS-X library function fsalloc converts Fortran strings to C strings. The function strtofs converts C strings to Fortran if you need to pass character string output back to the Fortran program that called this function jacket. Be sure to call the C function with the appropriate data types.

Writing shell scripts with McIDAS-X commands

A shell script is a program containing a set of executable commands. Shell scripts are useful for running a series of individual McIDAS-X commands outside of McIDAS-X. Generally, you must invoke the McIDAS-X resident program, mcenv, before running a series of McIDAS-X commands.

 

For information about writing shell scripts in McIDAS-X, see Appendix H, Running Commands Outside a McIDAS-X Session, in the McIDAS User's Guide.

Debugging McIDAS-X applications

It's a fact of life that programs do not always behave as expected. Therefore, you should implement some type of debugging strategy to isolate and repair errors. This section describes the tools available to help you debug applications. McIDAS-X provides two facilities to aid your search for the elusive bug. Additionally, Unix systems provide a variety of symbolic debugging tools, the most common of which is dbx or gdb.

Print statements

In McIDAS-X, the simplest debugging aide is ddest/Mcdprintf. These functions allow applications to print hidden statements when the third character in the DEV= global keyword is C. Strategically placing debug print statements in your code is an excellent way to identify invalid values or corrupted data in disk files. Use debug messages prudently, however, and avoid putting them inside large loops that may generate thousands of lines of useless messages.

 

For more information on ddest and Mcdprintf, see the section titled Text messages in Chapter 4, McIDAS-X Utilities.

M0ASSERT

Another debug facility provided in McIDAS-X is the C language development tool, M0ASSERT, which is used to trap severe errors in code, such as a memory overlap problem or trying to pass a NULL character pointer into a routine.

M0ASSERT is a macro that takes a boolean expression as input. If the expression resolves to false, a message is printed to stderr similar to this:

Assertion failed: source_file line nnn

where source_file is the name of the file and nnn is the line number in source_file where the failure occurred. After the message is printed, a call to exit(1) is made to end the program.

M0ASSERT can take two different forms based on compile-time options that you specify. The compile-time default for M0ASSERT is the NULL statement, meaning M0ASSERT does nothing. If you add the option -DM0ASSERT_ON to the compile statement, the preprocessor adds the assertion test.

M0ASSERT is not a substitute for standard error handling. Don't assume that capturing assertion failures will be activated as part of the production version of the software. Also, don't use M0ASSERT to test for conditions that, while rare, could indeed happen. The sample code fragment below demonstrates both good and bad uses of M0ASSERT.

int foo (const char *string)

/* if the NULL pointer is passed into foo, you have a big problem.
   This is a good example of M0ASSERT */

M0ASSERT(string != (const char *) NULL);

/* below is a bad use of M0ASSERT because memory allocation 
 * failure is quite possible */

ptr = (char *) malloc (10);
M0ASSERT (ptr != (char *) NULL);

 

For more information about assertions, read the excellent description provided in Chapter 2 of Writing Solid Code by Steve Maguire, Microsoft Press.

dbx

Most Unix-based systems include the symbolic debugger, dbx. To interactively debug McIDAS-X applications with dbx, you must perform a series of steps, which are necessary for these reasons:

Perform the steps below to set up your environment for interactive debugging. For this description, assume that the development source code is in ~/mcidas/dev, an application is in the source file foo.pgm, and it calls functions found in suba.c and subb.for.

  1. Copy a version of your source files into the ~/mcidas/data directory with the standard file extensions for the language.
    Type:   cp ~/mcidas/dev/foo.pgm ~/mcidas/data/foo.f
      cp ~/mcidas/dev/suba.c ~/mcidas/data/suba.c
      cp ~/mcidas/dev/subb.for ~/mcidas/data/subb.f

  2. Compile and link your functions and command with the -g option, which produces additional symbol table information necessary for the debugger. The resulting binary will be put in ~/mcidas/bin/foo.k.

  3. Change to the ~/mcidas/data directory.

    Type:  cd ~/mcidas/data

  4. Verify that your MCPATH environment variable is set up appropriately. At the minimum, MCPATH should contain the directories $HOME/mcidas/data and ~/mcidas/data.

  5. Start an environment for McIDAS-X applications to run under.

    Type:  mcenv

  6. Start the debugger.

    Type:  dbx ../bin/foo.k

  7. From the dbx prompt, activate the source code.

    Type:  list main0

  8. Input the McIDAS command parameters as you would running the command from a unix prompt.

Programming do's and don'ts

This section describes some of the common issues that McIDAS-X developers encounter. It will offer suggestions for avoiding platform dependency problems and for planning new software development.

Byte flipping

When writing data access applications that may reside on other machines, you must be aware of the differences in byte ordering between big-endian and little-endian machines. These differences are described in the section titled Conversion Utilities in Chapter 4 of this manual. You will use the functions swbyt2 and swbyt4 to switch the byte ordering.

Floating point numbers

Different platforms may use different methods of representing floating point numbers in memory. Do not store floating point numbers to disk in the native format of the machine if the data may be used on other platforms. Instead, store floating point numbers as scaled integer values.

Julian day representation for the 21st century

Historically, the McIDAS-X standard for representing days was to use the Julian day with two digits representing the year of the century in the yyddd format. For example, 1 January 1997 is 97001. From the user's perspective, the convention of the 2-digit year continues into the 21st century. McIDAS-X continues to determine the century by making the most reasonable choice: 97 indicates 1997 (not 2097), whereas 03 indicates 2003 (not 1903). The user can also input the full 4-digit year in the ccyyddd format.

For disk files we use the convention for year of the number of years past 1900. For example, 97 indicates 1997, 103 is for 203. The McIDAS-X library contains many functions for handling this new Julian day format. These functions are described in the section titled Conversion utilities in Chapter 4 of this manual. Use the new representation not only in memory-based applications but also in file structures.

Think globally

When writing software that deals with geographic locations, verify that your code works properly and efficiently on all quadrants of the globe. For example, even if you think your subsystem will only be used for Northern Hemisphere data, make it robust enough to work in the Southern Hemisphere as well.

Fortran-specific do's and don'ts

The suggestions below will help you when programming in Fortran.

  • Don't use a NULL string as a substitute for a blank character. For example, don't:

    call sdest('',0)
              

    Instead, do:

    call sdest(' ',0)
              
  • Don't concatenate strings of indeterminate lengths in parameter lists.

        integer function foo (string)
        character*(*) string
        character*80 tempstring
    
    c---    don't do this
    
        call sdest('The value of string is '//string,0)
    
    c---    do this instead
    
        tempstring = string
        call sdest('The value of string is '//tempstring,0)
    
  • Put data statements after integer, parameter, common declarations.

  • Use status= with an open statement. Use NEW, OLD or SCRATCH. The default for status= is usually UNKNOWN, which is defined as an implementation-dependent option. The sample code below shows you what to do.

    character*(MAXPATHLENGTH) cfile
    integer unitno
    parameter (unitno = 2)
    logical file_there
    ....
    inquire (file=cfile, exist=file_there)
    if (file_there)then
       open (unitno,file=cfile, status='OLD')
    else
       open (unitno,file=cfile, status='NEW')
    endif
    
  • Fortran compilers don't necessarily pad character literals out to column 72. For example, don't:

     call sdest ('this is a very long string I think this will
    &run way off the end',0)
    

    Instead, do:

     call sdest ('this is a very long string I think '//
    &'this will run way off the end',0)
    
  • Avoid memory overlapping when assigning character variables. For example, don't:

    string(2:20) = string(1:19)
    
  • Verify that you are passing the correct-length floating point representations into functions. For example, if foo is defined as:

    integer function foo (input1, input2)
    implicit none
    real input1
    double precision input2
    

    Make certain your code passes arguments to foo like this:

    ok = foo (7.0, 9.d0)
    
  • Avoid manipulating parameters passed in as function arguments directly. Many systems cause segmentation violation errors if the value passed in is a constant. For example:

    function foo (string)
    character*(*) string
    
    call mcupcase (string)  ! convert to uppercase
    

    If the call to foo looks like the one below, segmentation faults will usually occur because the mcupcase call cannot modify the constant Australia.

    status = foo ('Australia')
    
  • Don't write Fortran routines that return character strings as the function return code if you're planning to write C jackets for them.

    For example, don't:

    character*12 function foo (input)
    

    Instead, do:

    integer function foo (input, output_string)
    
  • Don't embed function calls in concatenation of character strings. For example:

    string = 'The temperature is '//cfr(temp)
    
  • If you're using formatted write statements, be sure the data type being printed matches the output declaration type. For example, the code below:

    REAL temp
    temp = 32.0
    write (string, FMT='(a20,i4)')'The temperature is ',temp
    call sdest(string,0)
    

    Will print The temperature is 0 on some platforms. Instead, use:

    write (string, FMT='(a20,f6.2)')'The temperature is ',temp
    
  • Fortran-77 requires that dimension variables' data type be known before encountering an array declaration that uses them. For example, don't:

    function foo (array, nrow, ncol)
    implicit none
    real array(nrow, ncol)
    integer nrow, ncol
    

    Instead, do:

    function foo (array, nrow, ncol)
    implicit none
    integer nrow, ncol
    real array(nrow, ncol)
    
  • The only recommended continuation character for column 6 is `&'.

  • Instead of declaring character constants like this:

    character*12 string
    parameter (string = 'Hello World!')
    

    Declare them like this:

    character*(*) string
    parameter (string = 'Hello World!')
    

C-specific do's and don'ts

The suggestions below will help you when programming in C.

  • Include string.h, not memory.h when using the mem * functions.

  • Variables with typedef integral types can be a problem when printing, since printf requires you tell it the size of the value. The solution is to cast all ambiguous types to long, as shown below.

    uid_t   u; /* usually unsigned */
    size_t  j; /* usually unsigned */
    ssize_t k; /* signed size_t */
    
    printf ("%ld %ld %ld\n", (long) u, (long) j, (long) k);
    
  • Don't use realloc (NULL, size) to be the same as malloc (size). Some compilers don't allow this convention.

  • Don't use malloc (0) or realloc (p, 0). It may return either a pointer to a segment size of 0 or a NULL pointer. The result is implementation-dependent.

  • Don't use strlen (p) where p may be NULL, as this causes segmentation violation errors on some platforms.

  • Don't assume the return value from sprintf is the length of the resulting string.

 


[Search Manual] [Table of Contents] [Go to Previous] [Go to Next]