function Gnash(GnashFile);	%Convert to a "function". Data is placed in the "global" area.
%   My first MatLab attempt, to swallow data as produced by Gnash's DUMP
%command in a comma-separated format. Such files start with a line
%beginning with a %, signifying a comment offering information on how the
%file was created, then follow two header lines, the first
%having a list of names for the columns, the second a list of titles.
%Then follows the mass of data, 17568 lines to a (leap) year. For example:
%
%     %Produced by Gnash from C:\Nicky\HH\BigBag.Gnash at 3:36:11.515pm. on Monday 6/ 6/2005 via dump Blenheim to b.csv
%     "Aux.Date","Aux.HHn","Aux.DayClock","Aux.Sun","Aux.DayClass","B"
%     "Date.","Half-hour number of the day.","Days since Sunday, 31'st December 1899.","Solar Altitude in Degrees at Mt. Cook Trig, Wellington.","Day Class.","Blenheim"
%     01/04/1981, 1  ,29676.0104,-52.87, 3,   9744.0
%     01/04/1981, 2  ,29676.0312,-52.68, 3,   9120.0
%     01/04/1981, 3  ,29676.0521,-51.30, 3,   8640.0
%   and so on to the end of the day
%     01/04/1981,48  ,29676.9896,-52.25, 3,  10704.0
%     02/04/1981, 1  ,29677.0104,-53.26, 4,   9744.0
%   etc.
%
%   Activate via
%      GnashFile('filename.csv');	%Specify the name of the file to read.
%      Gnash filename.csv		%Note that case counts; thus not "gnash".
%
%   The result will be the creation of variables
%      GnashCols,  GnashRows,      -describing the sizes.
%      GnashNames, GnashTitles,    -from the header lines.
%      GnashData,                  -The data.
%   plus a collection of variables based on the names found in the header,
%in structures corresponding to the name structures, if any, being in
%their aggregate a copy of GnashData, but structured, somewhat.
%
%   A column name of "Aux.DayClock" (exactly that) is recognised as being
%the time stamp as produced by Gnash, and the number is shifted so as to
%match matlab's usage of day numbers.
%   A column name containing "date" (capitals or lowercase) will be taken
%to mark calendar dates in the day/month/year style, and a conversion
%into matlab's day number will be attempted. Likewise, a column with
%a name containing "time" will provoke a conversion attempt via matlab's
%usage of "HH:MM", which also accepts am/pm styles. Since Gnash presents
%am/pm times with "Noon" and "Midn" to reduce confusion (though, this can
%be suppressed), these cases are recognised, and converted to "12:00am" and
%"24:00pm", which matlab accepts.
%   In response to those who choose odd times, a further feature.
%If the column name has the form "Time -1800" then the name will become "Time"
%and -1800 seconds (converted to a fraction of a day: -1800/86400) will be
%subtracted from all times, as might be appropriate if you wanted the time of
%the end of a half hour (which is the established usage) when what has been
%supplied is the time of the start of the half hour, or, something else.
%Notice the space after the "time" but not after the hyphen.
%   Should there be no DayClock column, an attempt to create one is made by
%combining the date and time fields, with the times of the time field shifted
%back by half a time step so as to attain the middle of the interval,
%as is the case for supplied DayClock values.
%   Daylight saving once again demands its tribute. Clock times fold back
%on the stretch day (which has 25 hours) and this routine expects to see the
%marker #2 appended to such repeated times, thus "2:00#2", replacing "am"
%if the am/pm style is used. The clock sequence runs
%     0:30am 1:00am 1:30am 2:00am 2:30am 2:00#2 2:30#2 3:00am, etc. as supplied.
%     0:30am 1:00am 1:30am 2:00am 2:15am 2:30am 2:45am 3:00am, etc. as fiddled.
%   The markers are expected in pairs, and are dealt with pairwise, though only
%the first of each pair is used. If it fingers index i, elements i - 1, i, and i + 1
%will be adjusted as per the above table, by -15, +30, +15 minutes respectively.
%
%   There is scant checking, and less provision for possible mishaps.
%The data files to be read are computer-prepared, and so should be in good order...

%   This should be a function, so that the file name is a parameter, but
%variables created within a function vanish when the function exits, or,
%if the "Global" feature of MatLab is tried, they have to be named both
%outside and inside the function, and in this case the names are not known
%until after the file is read. There may be hundreds.
%   What is wanted is a "macro" facility which behaves as a function with
%parameters, but runs at the same level as the invocation, and thus can
%create variables that the caller can use. Much later, further features
%of matlab were explored and indeed this became a function, allowing less
%annoyance with the invocation scheme and reducing the chances of name
%conflict but introducing puzzlement over the placement of results.
%   An oddity of matlab syntax allows the function style invocation to omit
%brackets, as in Gnash('filename') or just Gnash 'filename'.
%   A more practised twiddler of MatLab might be able to do better, but
%this will do for now.
%   Concocted by R.N.McLean (whom God preserve), 19'th May MMV.

		%I'm not too clear as to just what this does, given "global".
%               clear GnashCols GnashData GnashNames GnashRows GnashTime GnashTitles;
               global GnashCols GnashData GnashNames GnashRows GnashTime GnashTitles;	%Not just my little secret,
evalin('base','global GnashCols GnashData GnashNames GnashRows GnashTime GnashTitles;');%Let them be seen elsewhere also.
%evalin('base','clear  GnashCols GnashData GnashNames GnashRows GnashTime GnashTitles;');%I'm becoming confused.
%                clear GnashCols GnashData GnashNames GnashRows GnashTime GnashTitles;	%As to what is where and where I am.

%                   First, find the file and feel its heading.
if ~exist('GnashFile','var')	%Does this name exist?
 disp('Nominate the data file to Gnash by providing a parameter, for example, Gnash(''bigmeal.txt''); or, Gnash bigmeal.txt;');
 return;			%Abandon all.
end;				%So, we have a file name.
GnashIn = fopen(GnashFile,'r');	%So, have a go at it...
if GnashIn < 0			%Perhaps not with success.
 disp(['Try some other value for GnashFile! I can''t grab a file called ',GnashFile]);	%Oh well.
 clear GnashIn;			%Remove some rubble.
 return;			%Give up.
end;				%So much for getting hold of the file.
fprintf(2,['First, feel the head of file ',GnashFile,'\n']);	%Phrenology.
GnashRec = 0;			%This is where we're at.
GnashHic = true;		%Presume trouble.
for probe = 1:3		%Seek column names, column titles, and the first record of data.
 acard = fgetl(GnashIn);		%Grab the first line. Sigh. While Read(GnashIn) Text & Text[1:1] = "%" do Write text;
 GnashRec = GnashRec + 1;
 if ~ischar(acard) 		%Something read?      Where Read(in) namelist returns false if end-of-file.
  disp([' Can''t read line ',int2str(GnashRec),' of file ',GnashFile]);
  fclose(GnashIn); clear GnashIn acard;	%Remove some rubble.
  return;			%Give up.
 end;				%Righto, we have a line
 while strncmp(acard,'%',1)	%Is it a comment?
  disp([' Provenance: ' acard]);	%Yes. Show it.
  acard = fgetl(GnashIn);	%And lunge forward.
  GnashRec = GnashRec + 1;	%Counting as we go.
 end;				%So much for the start of the header. Now I expect columnwise data.
 Grist = strread(acard,'%q','delimiter',',');	%Comma-separated quoted strings, quotes not insisted upon..
 clear style;		%Contemplate the record's content.
 for col = 1:size(Grist,1)	%Step through the fields.
  if strncmp(Grist{col},'',1)		%A null field?
    style(col) = 0;			%Yes. Possibly this is an absent number.
  elseif size(strfind('+-.0123456789?',Grist{col}(1))) > 0	%This is how a number might start.
    style(col) = 0;				%So, declare a number.
    if size(strfind(Grist{col},'/')) > 0	%But if the field contains a slash, somewhere, it is probably a date.
      style(col) = 1;				%Thus.
      if size(strfind(Grist{col},':')) > 0	%Possibly a time, marked by a colon somewhere?
        style(col) = 3;				%Oh dear. A timestamp pair.
      end;					%Presumably, date then time.
    elseif size(strfind(Grist{col},':')) > 0	%No slash but a colon? Probably a time.
      style(col) = 2;				%Thus.
    end;				%So much for a field starting with a number.
  elseif size(strfind('"''',Grist{col}(1))) > 0	%But if not, a quote or apostrophe?
    style(col) = -1;		%This means a quoted string, but %q turns out to accept unquoted text also.
  else				%But of there is no quote, and it was not thought to be a number field,
    style(col) = -2;		%Declare a text field.
  end;				%Interpretation of the content is not my worry.
 end;				%On to the next field.
 switch probe		%What am I trying to do?
  case 1		%Acquire column names.
   GnashCols = size(Grist,1);	%So, how did the first go?
   disp([' Column count: ' int2str(GnashCols)]);	%Announce.
   GnashTooFew = GnashCols < 3;	%One likely problem.
   if GnashTooFew			%Encountered?
    disp(' A minimum of two fields per line are expected: one for time stamps, and at least one for data.');
   end;				%Though perhaps there is no interest in time stamps.
   if size(find(style >= 0),2) > 0	%Any numberish fields?
     disp([' Line ',num2str(GnashRec),' should have a set of column names, but some look like data! ']);
     for col = 1:GnashCols		%No heading of use, so...
      if col < 10			%Generate a column name.
        GnashNames{col} = ['Data.Col0',int2str(col)];	%This time I want I2.2
       else				%If there are more than 99,
        GnashNames{col} = ['Data.Col',int2str(col)];	%Too bad.
      end;				%This is the end of my patience.
      GnashTitles{col} = ['Column ',int2str(col)];	%And a column title.
      ShowStuff(GnashCols,col,style(col),Grist{col});	%Another ine of a table.
     end;				%On to the next column.
     break;			%Cease probing, we've struck a data record!
   end;				%Enough suspicion.
   GnashNames = Grist;		%Accept the record's content as supplying column names.
  case 2		%Acquire column titles.
   GnashTitles = Grist;		%And I will use this even if not title texts as such.
   if size(find(style >= 0),2) > 1	%Any numberish fields?
     disp([' Line ',num2str(GnashRec),' should have a set of descriptive texts, but some look like data instead! ']);
     for col = 1:size(Grist,1)		%Show my suspicions.
      ShowStuff(GnashCols,col,style(col),Grist{col});	%In nice aligned columns.
     end;				%Some hope that this will be read and understanding dawn.
     break;			%Cease probing as we've rammed into a data record..
   end;				%So much for suspect title texts.
  case 3		%Acquire the first line of data.
   GnashHic = false;		%All is well, and style has been decided upon.
 end;				%So much for the separate probe actions.
end;			%On to the next probe.
GnashRec = GnashRec - 1;	%The record in Grist has to be re-read in the mass reading of data.
GnashColsT = size(GnashTitles,1);	%There may be another problem.
GnashMisMatch = GnashCols ~= GnashColsT;	%They are expected to be the same.
if GnashMisMatch		%A token check.
 Grammur = [' Alas, the first line offered ',int2str(GnashCols),' name fields while the second offered ',int2str(GnashColsT),' title'];
 if GnashColsT ~= 1, Grammur = [Grammur,'s']; end;	%And now for some grinding.
 disp([Grammur,'.']);			%The message at last!
 clear Grammur;				%Begone!
end;				%So much for that.
clear GnashColsT;		%Discard the possibly deviant.
if GnashCols ~= size(Grist,1)	%Seeing as I have a data record...
 disp([' Alas, the first line offered ',int2str(GnashCols),' name fields while the first data line offers ',int2str(size(Grist,1)),' fields.']);
 GnashMismatch = true;			%Another way to make a mess.
end;				%Enough checking.
if GnashMisMatch || GnashTooFew || GnashHic	%Alright, offer a hint.
 disp(' ');				%Set off a bit.
 disp(' After possible comment lines (identified with a % in the first column) comes the grist:');
 disp(' The first grist line is expected to have N quoted text fields, naming the N fields per line to follow,');
 disp(' The second having N quoted text fields with corresponding titles for the N fields,');
 disp(' The third and subsequent lines having N fields per line; all this somewhat as follows, with N = 6:');
 disp(' ');
 disp(' %Produced by Gnash from C:\Nicky\HH\BigBag.Gnash at 3:36:11.515pm. on Monday 6/ 6/2005 via dump Blenheim to b.csv');
 disp(' "Aux.Date","Aux.HHn","Aux.DayClock","Aux.Sun","Aux.DayClass","Blenheim."');
 disp(' "Date.","Half-hour number of the day.","Days since Sunday, 31''st December 1899.","Solar Altitude in Degrees at Mt. Cook Trig, Wellington.","Day Class.","Blenheim"');
 disp(' 01/04/1981, 1  ,29676.0104,-52.87, 3,   9744.0');
 disp(' 01/04/1981, 2  ,29676.0312,-52.68, 3,   9120.0');
 disp(' ...etc.');
 disp(' ');				%So much for the hint.
 frewind(GnashIn);			%Back to the start of the file.
 disp([' The first ' int2str(GnashRec + 6) ' lines of file ',GnashFile,' are as follow:']);
 for i = 1:GnashRec + 6			%Deliver on my promise.
  acard = fgetl(GnashIn);		%Slurp.
  if ~ischar(acard), break, end;		%Daft syntax!
  disp(['  Line ',int2str(i),': ',acard]);	%Take that!
 end;					%On to the next line.
 disp(' ');				%Set off.
 disp(' Success is hoped for: onwards to find what will fail first.');	%Just so.
end;				%Enough blather.
%Thanks to grabbing the first line of data, we're always out of step, unless a line-by-line reading of data were devised...
disp([' Re-positioning so as to start eating data with line ' num2str(GnashRec + 1),'.']);	%Ho hum.
frewind(GnashIn);		%In the absence of a Find(GnashIn,Record = GnashRec) or similar.
for i = 1:GnashRec		%Step through the file.
 acard = fgetl(GnashIn);		%Record by record.
end;				%On to the next.
clear GnashMisMatch GnashTooFew GnashHic GnashPoke;	%Discard the assistants, and proceed.

%                 Contemplate the grist, to prepare a meal menu. Leading spaces seem to be removed, also quotes around texts.
ln = 4;			%I want the maximum length of the names.
ld = 8;			%And of the fields of the first record's data.
for col = 1:GnashCols		%But I can't guess how to ask...
 ln = max(ln,size(GnashNames{col},2));	%So, one by one, damnit.
 ld = max(ld,size(Grist{col},2));	%This is tedious...
end;				%And on to the next.
fprintf(2,['Second, inspect the data from line ',int2str(GnashRec + 1),' to guess at formats.\n']);
disp([' Column Name',blanks(ln + ld - 11),'1stDatum type Title']);	%A heading.
iDate = 0;		%I know of no date column.
iTime = 0;		%Nor of a time column.
iHHn = 0;		%Nor an auxiliary time.
iDayClock = 0;		%Nor a combined date and time as supplied by Gnash.
iDT = 0;		%Nor one identified as a date and time as such.
Toff = 0;		%Nor a time offset.
offset(1:GnashCols) = 0;
idiot = '';		%A scratchpad to prepare a "format", without benefit of repetition counts.
for col = 1:GnashCols	%Step through each field.
 [PartA,PartB] = strtok(GnashNames{col});	%The text might be "Time -30" for example. "Time-30" confuses the idiot strtok, damnit.
 if ~strcmp(PartB,'')		%So, is there an addendum?
   try				%Yes. Now for some vexatious gibberish.
     offset(col)= strread(PartB,'%f');	%Convert to a number, taken to be seconds.
     GnashNames{col} = PartA;		%Retain the "Time" as the name.
    catch				%Or perhaps that wasn't a good number.
     offset(col) = 0;			%So, no offset.
   end;					%Enough of that.
 end;				%So much for any offset.
 switch style(col)	%So, what had I decided upon?
  case -2			%A text string, no quotes.
   it = '%s';				%It is still not like a number, so I'll declare it a string.
  case -1			%A quoted text string,.
   it = '%q';				%This accepts unquoted text also.
  case 0			%A number.
   it = '%f';				%There is no style for dates, etc.
  case 1			%So I have to recognise them.
   it = '%s';				%And convert them from text strings.
   if iDate <= 0			%Have I found a date-ish name already?
     iDate = col;			%No. Remember this one.
    else				%But otherwise,
     disp(['        A date in column ' int2str(iDate) ' was first!']);	%Scowl.
   end;					%So much for a date column.
  case 2			%A time.
   it = '%s';				%The text string will be reinterpreted later.
   if iTime > 0				%Have I found a candidate already?
     disp(['A time in column ' int2str(iTime) ' was first!']);	%Quite.
    else				%Select the first such for special recognition.
     iTime = col;			%Recall this place.
     Toff = offset(col);		%So much for any offset, presumed seconds here.
     clear PartA PartB;			%Discard the help.
   end;					%So much for a time field.
  case 3			%Date and time together.
   it = '%s';				%This should be investigated further.
   if iDT <= 0
     iDT = col;
   end;
 end;				%Enough of the differences.
 idiot = strcat(idiot,it);	%Append the latest drivel. NO repetition counts in this idiot scheme.
 if strfind(upper(GnashNames{col}),'DAYCLOCK')	%While I'm at it,
  iDayClock = col;			%Identify certain names.
  GnashNames{col} ='Aux.DayClock';	%Ensure consistent case for "Aux".
 elseif strfind(upper(GnashNames{col}),'HHN')	%I recognise a half-hour number.
  iHHn = col;			%One such has been found.
  GnashNames{col} = 'Aux.HHn';	%Regularise the name.
 end;				%So much for a time column.
 damn = num2str(col);	%Nothing I can think of generates a fixed-size field, as in furrytran's I6 format.
 damn = [blanks(7 - size(damn,2)),damn];%So, damn stupid fiddling results.
 i2 = num2str(style(col));		%And this too.
 if size(i2,2) <= 1, i2 = [' ',i2]; end;%So much for I2 format.
 disp([damn,' ',GnashNames{col},blanks(ln - size(GnashNames{col},2)),blanks(ld - size(Grist{col},2) + 1),Grist{col},' ',i2,' ',idiot(2*col),' ',GnashTitles{col}]);	%On the way, announce the name and title.
end;		%On to the next column.
clear damn i2 ln ld;
if iDayClock == 0 && iDT > 0		%No supplied DayClock (Gnash style), but a date & time field?
 GnashNames{iDT} = 'Aux.DayClock';	%Yes!
end;					%This will have a mutlub style day number.

%Another source of data, another scheme, so some more semi-generality.
if iDate > 0			%Do we have a declared date?
  if size(find(style == 1),2) == 1	%Just one?
    GnashNames{iDate} = 'Aux.Date';	%Yes. Convert to my name scheme.
  end;					%Ready for the arrival of data.
 else					%If no explicit date, perhaps we have something else?
  iYear = 0; iMonth = 0; iDay = 0;	%These might be combined.
  for col = 1:GnashCols			%Perhaps there are individual columns.
   if strfind(upper(GnashNames{col}),'YEAR'),  iYear  = col; end;	%Surely there is a better way?
   if strfind(upper(GnashNames{col}),'MONTH'), iMonth = col; end;	%This is becoming tiresome.
   if strfind(upper(GnashNames{col}),'DAY'),   iDay   = col; end;	%Slogging along.
  end;					%So much for the search.
  if iYear*iMonth*iDay > 0	%So, do we have all three?
    disp([' No DATE column, but I see separate Year, Month, and Day columns: ',int2str(iYear),', ',int2str(iMonth),', ',int2str(iDay)]);
    GnashNames{iYear}  = 'Aux.Year';	%Convert the recognised
    GnashNames{iMonth} = 'Aux.Month';	%Into my naming scheme.
    GnashNames{iDay}   = 'Aux.Day';	%Because I devised it.
  end;					%It seems a good idea to me.
end;				%So much for clutching at dates.
if iTime > 0			%Has a time been declared?
  if size(find(style == 2),2) == 1	%Yes. Just the one?
    GnashNames{iTime} = 'Aux.Time';	%Take over its column name.
  end;					%To encourage consistency.
 else				%We lack a declared time.
  iHour = 0; iMin = 0; iSec = 0;	%Alas.
  for col = 1:GnashCols			%Perhaps some columns may be helpful.
   if strfind(upper(GnashNames{col}),'HOUR'),  iHour = col; end;	%Agh.
   if strfind(upper(GnashNames{col}),'MINUTE'),iMin  = col; end;	%Here we go
   if strfind(upper(GnashNames{col}),'SECOND'),iSec  = col; end;	%Again.
  end;					%I have sought.
  if iHour*iMin*iSec > 0	%Did I find all three? (Hopefully, no multiple appearances)
    disp([' No TIME column, but I see separate Hour, Minute and Second columns: ',int2str(iHour),', ',int2str(iMin),', ',int2str(iSec)]);
    GnashNames{iHour} = 'Aux.Hour';
    GnashNames{iMin}  = 'Aux.Minute';
    GnashNames{iSec}  = 'Aux.Second';
  end;				%So much for an alternative.
end;		%Hopefully, these name twiddles won't provoke annoyance below.

%   Pander to some problems matlab has with structure names.
%   A name such as X.Y is acceptable to matlab, but X.666 is not.
% So, insert the letter v as a prefix to such digits.
%   Secondly, if X.Y is a name, matlab dislikes the use of X as a name in
% the same way also since X is to be the name of the collective in its view...
% Accordingly, the name X is to be changed to X.itself, in the hope that no
% such name will be supplied by the input data. Since matlab distinguishes
% between capitals and lowercase in names, and I expect that most name parts
% will start with a capital, all should be safe, if potentially confusing and
% vexing. As to the means of identifying such names, Gnash supplies the
% names in sorted order (except for the "Aux" names at the start, which
% don't have this problem), treating lower case characters as capitals,
% thus a name X will be followed by X.Y and so the troublesome condition can
% be recognised by comparing names pairwise.
GnashNamesOriginal = GnashNames;	%Some fiddles impend.
GnashNames = strrep(GnashNames,':','.');	%Gnash may generate : in names, if no alias table were active.
if ~isequal(GnashNamesOriginal,GnashNames)	%Any change?
 disp(' Alas, MatLab dislikes names containing colons. I convert such to .');
 GnashNamesOriginal = GnashNames;	%A further test impends.
end;		%There may be other troublesome parts...
GnashNames = strrep(GnashNames,'#','.');	%Gnash may generate # in names, as for ad-hoc expressions.
if ~isequal(GnashNamesOriginal,GnashNames)	%Any change?
 disp(' Alas, MatLab dislikes names containing #. I convert such to .');
 GnashNamesOriginal = GnashNames;	%A further test impends.
end;		%There may be other troublesome parts...
GnashNames = strrep(GnashNames,'$','Dollar');	%Gnash allows $ in names, and they may be used elsewhere.
if ~isequal(GnashNamesOriginal,GnashNames)	%Any change?
 disp(' Alas, MatLab dislikes names containing "$". I convert such to "Dollar"');
 GnashNamesOriginal = GnashNames;	%A further test impends.
end;		%There may be other troublesome parts...

for i = 0:9	%Try every possible digit. I'm adrift in multiple matlab twiddle routines.
 GnashNames = strrep(GnashNames,['.',int2str(i)],['.v',int2str(i)]);    %Crude, but I'm irked.
end;		%So "Albany.110KV" becomes "Albany.v110KV", alas...
if ~isequal(GnashNamesOriginal,GnashNames)	%Any change?
 disp(' Alas, matlab dislikes name parts starting with a digit. I supply a "v" prefix for such...');	%Sigh.
 GnashNamesOriginal = GnashNames;	%A further test impends.
end;		%Crude detection of deeds.

GnashNames = strrep(GnashNames,'..','.');	%Gnash allows .. in names, normally suppressed via its alias list.
if ~isequal(GnashNamesOriginal,GnashNames)	%Any change?
 disp(' Alas, MatLab dislikes names containing .. I convert such to .');
 GnashNamesOriginal = GnashNames;
end;		%There may be other troublesome parts...

GnashNames = strrep(GnashNames,' ','_');	%Spaces are troublesome.
if ~isequal(GnashNamesOriginal,GnashNames)	%Were there any?
 disp(' Alas, MatLab dislikes names containing spaces. I convert such to _');
end;
clear GnashNamesOriginal;	%Discard the hired help.

for col = 2:GnashCols		%Level tangles. If there is a "Fred.x" a "Fred" is not allowed.
 l = size(GnashNames{col - 1},2) + 1;	%It is known that the names from Gnash are in sorted order.
 if strncmpi([GnashNames{col - 1},'.'],GnashNames{col},l)	%Miscegnation? The "col" in strncmpi sez "ignore case".
  disp([' Alas, names ',GnashNames{col - 1},' and ',GnashNames{col},' can''t co-exist in matlab, so I append ".itself"']);	%Sigh.
  GnashNames{col - 1} = [GnashNames{col - 1},'.itself'];	%Yes. The parent came first.
 end;		%Thus, any "Fred" (followed by "Fred.x") becomes a "Fred.itself".
end;	%And on to the next.

for col = 1:GnashCols			%Inspect for an annoyance, such as "Blabber.Std.Dev.".
 l = size(GnashNames{col},2);		%First, the length of the text.
 if strcmp(GnashNames{col}(l:l),'.')	%Is the last character a . ?
  GnashNames{col} = GnashNames{col}(1:l - 1);	%Yes. Discard it.
  disp([' Column ',int2str(col),': Removed a trailing . from ',GnashNames{col}]);	%Note the deed.
 end;					%So much for that petty detail.
end;					%On to the next column name.

for col = 1:GnashCols			%Duplicated names cause confusion.
 e = 1;					%The first occurrence counts as one.
 for j = col + 1:GnashCols		%So check for later occurrences...
  if isequal(GnashNames{col},GnashNames{j})	%This one?
   e = e + 1;					%Thus provide a .v2, etc.
   disp(['Column ',int2str(col),' has the same name as column ',int2str(j),'! Appending .v',int2str(e),' to ',GnashNames{j}]);
   GnashNames{j} = [GnashNames{j},'.v',int2str(e)];	%Gurgle.
  end;					%So much for a match.
 end;				%On to the next check.
 if e > 1, GnashNames{col} = [GnashNames{col},'.v1']; end;	%Matlab whines over name and name.v2.
end;				%On to the next column.
clear e j;

%                 Devour the data. Swallow everything into GnashData.
fprintf(2,['Third, starting with line ',int2str(GnashRec + 1),' chew into the body of file ',GnashFile,'\n']);	%Here we go!
disp([' The idiot format specifier is ',idiot]);	%Try looking at Fortran's format specifications, with factoring.
clear GnashRec;				%I lose count now.
GnashData = textscan(GnashIn,idiot,'Delimiter',',','TreatAsEmpty','?','CommentStyle','%');	%Slurp!
fclose(GnashIn); clear GnashIn;		%Presume success.
GnashRows = size(GnashData{1},1);	%How much success?
disp([' Burp! Number of rows: ' int2str(GnashRows)]);	%Announce.
clear acard;				%Discard the assistants.
if GnashRows < 1 			%Success might be minimal.
  disp(' A shortage of grist!');	%The file just has a header?
  return;				%I give up!
end;					%Or, perhaps the format is ungood.
if GnashCols ~= size(GnashData,2)	%Another check.
  disp([' but the number of columns in GnashData is ',int2str(size(GnashData,2))]);	%Huh?
end;		%Token check for coherence.

%           Extract the cargo from GnashData, creating names as per GnashNames.
fprintf(2,'Fourth, cargo extraction.\n');
DayZero = datenum(1899,12,31);	%Gnash reckons 1/1/1900 as day one.
for col = 1:GnashCols		%Step along the columns.
 if style(col) > 0		%Some are to be re-interpreted.
   switch style(col)		%This could be done with tricky arrays.
    case 1				%But mutlub doesn't help.
     scrag = 'dd/mm/yyyy';		%This is for a date alone. Note that DD/MM/YYYY evokes odd results!
     thing = 'date';			%And I hope variations such as d/m/yy are accepted too.
    case 2				%Beause I don't want to have to mess about,
     scrag = '';			%As is needed for time specifications.
     thing = 'time';			%Happily, this is for a time alone.
    case 3				%But for a date followed by a time,
     scrag = 'dd/mm/yyyy ';		%The messing escalates.
     thing = 'date and time combined';	%And I don't want to worry over mixtures.
   end;				%There should be  better way.
   damn = char(GnashData{col}(1));	%Grab the first entry for inspection.
   plan = ['      Column ',int2str(col),' has a ',thing,' and its first value is ',damn,'; '];
   live = find(strncmp(GnashData{col},'',1)==0);	%Null strings should generate ? as the result.
   dead = find(strncmp(GnashData{col},'',1)==1);	%But oh no, mutlub abandons the attempt.
   if size(dead,1) <= 0			%These should not appear.
     scowl = '';			%And if they don't, I don't mention them.
    else				%But if they do...
     scowl = ['      Oh dear. Column ',int2str(col),' has ',int2str(size(dead,1)),' null field'];	%I am not happy.
     if size(dead,1) > 1,scowl = [scowl,'s']; end;			%And I grind at grammar.
     scowl = [scowl,'! The first is at row ',int2str(dead(1)),' for ',GnashNames{col}];	%To express displeasure properly.
   end;					%So much for premonitions.
   if style(col) == 1		%A date alone?
     disp([plan,scrag,' might be a good format to convert from a text.']);
     if size(scowl) > 0, disp(scowl); end;
     Time(live) = datenum(GnashData{col}(live),scrag,1950);	%dd/mm/yyyy accommodates d/m/yy variants as well.
     Time(dead) = NaN;			%Otherwise, who knows what the date is?
     GnashData{col} = Time';		%Can convert wholesale, not piecemeal.
     clear Time live dead;		%Enough of that.
     disp('      Done. "All is number" - Pythagoras.');	%But, what is "number"?
     if col == iDate, Aux.Date = GnashData{col}; end;	%Whatever, take it.
   elseif style(col) >= 2		%Times require detailed decoding.
     idiot = damn;			%This is really vexatious.
     if style(col)==3			%And this is worse.
       l = strfind(idiot,':') - 2;	%Find the start of the time text
       idiot = idiot(l:end);		%From its first colon.
     end;				%The date stuff should now be absent.
     idiot = strrep(upper(idiot),'AM','');	%For a mercy, MatLab recognises this without prompting.
     idiot = strrep(upper(idiot),'PM','');	%So these complications can be dodged.
     horror = strfind(GnashData{col},'#2');	%Unbounded vexation.
     ihate = 0;				%Compounded by bloody matlab and my ignorance of its brilliances.
     for j = 1:size(horror,1)		%What I want is just an array of the indices of the matched elements.
      if size(horror{j},1) > 0		%But no, what I get is an array of "cells",
       ihate = ihate + 1;		%Each of which has a list of matches for its index.
       hhate(ihate) = j;		%So, most entries are null.
      end;				%But this fishes out what I want.
     end;				%From what is provided.
     clear horror;			%This is only the first stage.
     if ~strcmp(idiot,damn)		%If am/pm appeared, there may be decorations.
       GnashData{col}(strmatch('NOON',upper(GnashData{col}))) = cellstr('12:00am');	%Supply
       GnashData{col}(strmatch('MIDN',upper(GnashData{col}))) = cellstr('24:00pm');	%Machinethink.
       for j = 1:ihate			%Having found some horror,
        GnashData{col}(hhate(j)) = strrep(GnashData{col}(hhate(j)),'#2','am');	%Conceal it.
       end;				%On to the next: re-searching seems wasteful.
      else				%If not am/pm style,
       for j = 1:ihate			%The horror may still lurk.
        GnashData{col}(hhate(j)) = strrep(GnashData{col}(hhate(j)),'#2','  ');	%Scrag. A null string is too short for "HH:MM".
       end;				%This is just the first stage.
     end;				%The horror spreads its tentacles.
     for j = 0:9			%Try every possible digit. I'm adrift in multiple matlab twiddle routines.
      idiot = strrep(idiot,int2str(j),'F');	%Crude, but I'm irked.
     end;					%So perhaps "FF:FF:FF.FFFFFFF" is in hand...
     if idiot(1:2)=='F:', idiot = ['F',idiot]; end;	%Or, was it "F:" etc?
     clear j;				%Enough of that.
     if idiot(1:3)=='FF:', idiot(1:2) = 'HH'; end;	%Now produce "HH:MM:SS".
     if idiot(3:5)==':FF', idiot(4:5) = 'MM'; end;	%Surely not hours only?
     if size(idiot,2) >= 8		%Possibly, with seconds also?
      if idiot(6:8)==':FF', idiot(7:8) = 'SS'; end;	%Space enough to sniff.
     end;				%This is really tiresome.
     scrag = [scrag,idiot];		%Yeah.
     disp([plan,scrag,' might be a good format to convert from a text.']);		%The plan.
     if size(scowl) > 0, disp(scowl); end;
     l = strfind(idiot,'.');		%Locate the start of trouble.
     if size(l,2) == 0 | length(idiot) == l + 3	%Not found, or, found ".FFF" exactly.
       Time(live) = datenum(GnashData{col}(live),scrag,1950);	%Grind.
       Time(dead) = NaN;		%No grind.
       if style(col) == 2		%A time alone presents trouble, thanks to mutlub.
        Time(live) = Time(live) - datenum('00:00','HH:MM');	%datenum(h=0,m=0,s=0) is NOT 0, but the day number of Jan 1'st of this year.
       end;				%Thus convert the time of day to a day fraction only.
       GnashData{col} = Time';		%Convert the style.
       disp('Size'); disp(size(GnashData{col}));
       clear Time live dead;		%Enough of that.
      else				%Only .FFF is recognised, damnit.
       disp('      Alas, MATLAB allows only milliseconds (.FFF) as that is the precision limit of its date&time value.');
       idiot1 = idiot(1:l - 1);		%The floundering begins. Snip off the '.FFFF' part.
       snarl1 = datenum(GnashData{col},idiot1) - datenum('00:00','HH:MM');	%Grab the HH:MM:SS part only.
       idiot2 = ['%',int2str(l - 1),'s%f'];		%Now go for the .FFFFFF part.
       for j = 1:GnashRows				%I've had enough of floundering. There should be a better way.
        snarl2 = textscan(char(GnashData{col}(j)),idiot2);	%This produces two parts, as there is no "skip" format.
        snarl3(j) = snarl1(j) + snarl2{1,2}/86400;	%At last, get at the full fraction of a second.
        if mod(j,10000) == 0, disp(['        ',int2str(j),' and grinding...']); end;	%Slow progress here.
       end;						%And on to the next.
       GnashData{col} = snarl3.';		%Where I want the result. Transposed, for whatever mad reason.
       clear idiot1 idiot2 snarl1 snarl2 snarl3;	%Clear the field of the unwanted.
     end;					%All of this struggle is needless. Damn matlab.
     if ihate > 0			%Had some horror been encountered?
       disp('      Damn daylight saving! Clock times are marked as folding back. The Hilbert Hotel is opened.');
       for j = 1:+2:ihate		%The Hilbert Hotel time shifts for the stretchdays.
        GnashData{col}(hhate(j) - 1) = GnashData{col}(hhate(j) - 1) - 15/(24*60);	%2:30   becomes 2:15
        GnashData{col}(hhate(j)) = GnashData{col}(hhate(j))         + 30/(24*60);	%2:00#2 becomes 2:30
        GnashData{col}(hhate(j) + 1) = GnashData{col}(hhate(j) + 1) + 15/(24*60);	%2:30#2 becomes 2:45.
       end;				%Thus cram in the extra two values with ascending times.
     end;				%Ah, but what of other time steps?
     clear ihate hhate;			%Not that my hate is cleared.
   end;				%Enough style variations.
   if Toff ~= 0			%Still further messing?
     disp(['      Subtracting the supplied time offset (presumed seconds) of ' num2str(Toff)]);	%Yes.
     GnashData{col} = GnashData{col} - Toff/86400;	%Shift by something.
   end;					%Enough time shifting.
 end;				%So much for the special cases.
 if iDayClock == col		%Now, another nusiance.
   if DayZero ~= 0		%Alas, mutlub uses something wierd.
    idiot = ['      Gnash uses 31/12/1899, a Sunday, as day zero but matlab numbers it ',int2str(DayZero),'. So, a re-base'];	%Grr.
    if mod(DayZero,7) == 0	%An additional consideration.
      idiot = [idiot,'.'];	%Perhaps not offended.
    else			%But actually, I know it is.
      idiot = [idiot,', even though (',int2str(DayZero),' mod 7) is not zero.'];	%Grr!
    end;			%So much for my disfavour.
    disp(idiot);		%Express it.
    GnashData{col} = GnashData{col} + DayZero;	%Some shift or other.
    GnashTitles{col} = 'Day and fraction of day, MatLab-style counting.';
   end;			%So much for the date shift.
   dt = (GnashData{col}(2) - GnashData{col}(1))*1440;	%Day fraction to minutes.
   if dt > 2			%Looks like a step of more than one minute?
    disp(['      The first time step is ',num2str(dt),' minutes; rounding the times to the nearest half minute...']);
    GnashData{col} = fix(GnashData{col}) + round((GnashData{col} - fix(GnashData{col}))*2880)/2880;	%Thus. (Daylight saving Hilber Hotel: thus half-minute.)
   end;	%Might as well have correct times...
   clear dt;	%And clear the assistant.
 end;		%So much for the fiddles.
 if size(strfind(GnashNames{col},'.TimeStamp'),1)
   disp(['      Adding ',int2str(DayZero),' to convert to a matlab-style day number!']);
   GnashData{col} = GnashData{col} + DayZero;
 end
 idiot = [GnashNames{col},' = ','GnashData{',int2str(col),'};'];	%Concoct an assignment.
 disp([' GnashData{',int2str(col),'} to ',GnashNames{col}]);		%Announce intent.
%heyu = [GnashNames{col},' = zeros(',int2str(size(GnashData{col},1)),');'];	%Prepare the preparer of the way.
%disp([' ',idiot]);		%Declare intent.
%disp([' ',heyu])		%Are we confused?    Subsequently, zeros(303953) was declared too big,
%evalin('base',heyu);		%Prepare the way.    Yet the assignment of that amount of data went ahead.
 evalin('base',idiot);		%Do the deed.        And did not seem to be delayed by array re-assignment.
end;			%And on to the next column.
clear i j l col heyu scrag thing damn idiot;

%                                Coordinate the clock information.
fprintf(2,'Fifth, contemplate the collation of clock and date information.\n');
%Another source of data, another scheme, so some more semi-generality.
if iDate == 0			%Do we lack a declared date?
  if iYear*iMonth*iDay > 0	%Yes. So, do we have all three?
    disp([' Forming a DATE column from the separate Year, Month, and Day columns: ',int2str(iYear),', ',int2str(iMonth),', ',int2str(iDay)]);
    GnashCols = GnashCols + 1;		%Also, I shall create a new column.
    GnashNames{GnashCols} = 'Aux.Date';	%Thus.
    GnashTitle{GnashCols} = 'Date (mutlub day number)';
    iDate = GnashCols;			%This is where it is.
    GnashData{GnashCols} = datenum(GnashData{iYear},GnashData{iMonth},GnashData{iDay});	%Convert.
    evalin('base',['Aux.Date = GnashData{',int2str(GnashCols),'};']);	%Maintain alignment.
  end;					%I could scrub the year, month, and day columns. Tricky.
  clear iYear iMonth iDay;		%I can scrub these easily.
end;				%So much for clutching at dates.
if iTime == 0			%Do we lack a declared time?
  if iHour*iMin*iSec > 0	%Alas. Did I find all three? (Hopefully, no multiple appearances)
    disp([' Forming a TIME from the separate Hour, Minute and Second columns: ',int2str(iHour),', ',int2str(iMin),', ',int2str(iSec)]);
    GnashCols = GnashCols + 1;		%Prepare another column.
    GnashNames{GnashCols} = 'Aux.Time';	%The "standard" name.
    GnashTitle{GnashCols} = 'Time (day fraction)';
    iTime = GnashCols;			%Here it is.
    GnashData{GnashCols} = ((GnashData{iHour}*60 + GnashData{iMin})*60 + GnashData{iSec})/86400;	%Whee!
    evalin('base',['Aux.Time = GnashData{',int2str(GnashCols),'};']);	%Augment.
  end;					%Clearing parts of GnashData looks tricky.
  clear iHour iMin iSec;		%Especially if avoiding mass copying.
end;		%So much for the alternative..

FoundDayClock = 0; FoundDate = 0; FoundHHn = 0; FoundTime = 0;	%First, we have no bananas.
for col = 1:GnashCols		%Inspect all the column names.
  if col == iDayClock			%The identified day clock?
    FoundDayClock = col;		%Yes, or so I hope.
    disp([' Column ',int2str(col),': Aux.DayClock found.']);
    GnashTime = GnashData{col};		%Day and time of day combined.
  elseif col == iDT
    GnashTime = GnashData{col};
    FoundDayClock = col;
  elseif col == iDate			%An identified date column? (The first one seen)
    FoundDate = col;			%Yes.
    disp([' Column ',int2str(col),': DATE found, presumably now already a matlab day number.']);
    GnashDate = GnashData{col};		%For possible combination with
  elseif col == iHHn			%An identified half-hourly number column?
    FoundHHn = col;			%Yes.
    disp([' Column ',int2str(col),': Aux.HHn found.']);
    GnashTime = GnashData{col}/48;	%Thus produce END times of a day, and smash any earlier GnashTime.
    if FoundTime > 0, disp(['These HHn end times replace the TIME values from column ',int2str(FoundTime)]); end;
  elseif col == iTime			%An identified time column?
    FoundTime = col;			%Yes.
    disp([' Column ',int2str(col),': TIME found, presumably now already a fraction of a day.']);
    GnashTime = GnashData{col};		%Gnash.m will already have converted this to a time of day number.
    if FoundHHn > 0, disp([' These TIME values replace the HHn times from column ',int2str(FoundHHn)]); end;
  end;				%Surely enough options already?
end;		%On to the next column.
if FoundDayClock == 0		%Did we find a combined clock value?
 disp(' Can''t see Aux.DayClock in the array GnashNames!');	%Nope.
 if FoundDate*(FoundTime + FoundHHn) <= 0	%Perhaps some associated variables?
   disp(' Alas, no sign of both a date and a time variable.');	%Oh well.
  else				%But, we may be in luck...
   disp([' Creating Aux.DayClock in column ',int2str(GnashCols + 1),' by combining the date and time variables...']);	%This might even work properly.
   GnashTime = GnashTime + GnashDate;	%Hopefully, matching and proper values.
   if size(GnashTime,1) > 2	%Have we enough values to mess about with?
     if all(GnashTime(2:end) - GnashTime(1:end - 1) > 0);	%There should be none of this.
       dt = GnashTime(2) - GnashTime(1);	%Find the first time step, here known to be positive.
       dt2= GnashTime(3) - GnashTime(2);	%And the next.
       if abs(log(dt2/dt)) > 0.25		%Are the two advances similar?
         disp([' The first two time steps are ',num2str(dt*86400),' and ',num2str(dt2*86400),' seconds: no adjustment to attain middle times, then.']);
        else				%But if the steps look similar,
         dt = dt/2;				%Prepare a fiddle.
         disp([' Presuming the times to be for the ENDS of the intervals, attaining MIDDLE times through subtracting ',num2str(dt*86400),' seconds.']);
         GnashTime = GnashTime - dt;		%Apply the fiddle.
       end;				%Righto, enough guesswork.
      else			%The times are suspect.
       hic = find(GnashTime(2:end) == GnashTime(1:end - 1));	%Look for zero steps.
       n = size(hic,1);			%How many?
       if n > 0					%Are there any?
        how = [' from element ',int2str(hic(1)),' to ',num2str(hic(1) + 1),' for ',num2str(GnashTime(hic(1))),' = ',datestr(GnashTime(hic(1)),'dd-mmm-yyyy HH:MM:SS.FFF')];
        if n == 1				%More grammar.
          disp([' A zero time step',how]);	%Just one.
         else					%But if there are many,
          disp([' The first of ',int2str(n),' zero time steps is',how]);	%Just finger the first.
        end;					%Along with the count.
       end;				%Damn mutlub doesn't round to the second. 31.993 secs. comes forth as 31.
       hic = find(GnashTime(1:end - 1) > GnashTime(2:end));	%Look for backsteppers.
       n = size(hic,1);			%Their number.
       for i = 1:min(n,12)		%Are there any?
        dt = GnashTime(hic(i)); dt2 = GnashTime(hic(i) + 1);	%Yes. Save on some typing.
        dd = dt - dt2;			%The backstep (positive), in days.
        if dd > 1			%Not one?
          hop = [num2str(dd),' days'];	%So, grammar allows s.
         else				%And for one day or less,
          hop = [num2str(dd*86400),' second'];	%There are many seconds.
          if dd*86400 ~= 1, hop = [hop,'s']; end;	%Except when there is exactly one...
        end;				%Enough of that.
        how = [' from element ',int2str(hic(i)),' to ',int2str(hic(i) + 1),' for ',num2str(dt),' = ',datestr(dt,'dd-mmm-yyyy HH:MM:SS.FFF'),' to ',num2str(dt2),' = ',datestr(dt2,'dd-mmm-yyyy HH:MM:SS.FFF')];
        disp([' A negative time step',how,' (of ',hop,')']);	%Just one.
       end;				%Enough complaints over backstepping.
       if n > 12, disp(['  ... and ',int2str(n - 12),' more!']); end;	%There could be many!
     end;				%Enough of suspect times.
     clear dt dt2 dd hic hop how;	%Discard the helpers.
   end;				%Enough suspicion.
   GnashCols = GnashCols + 1;			%Add another column...
   GnashNames{GnashCols} = 'Aux.DayClock';	%The standard name.
   GnashTitles{GnashCols} = 'Day and fraction of day combined, MatLab-style counting.';
   GnashData{GnashCols} = GnashTime;		%Whee!
   evalin('base','Aux.DayClock = GnashTime;');	%Create the name, create the corresponding branch.
   clear dt dt2 hic;		%Discard the hired help.
 end;				%So much for date and time.
end;			%We've figured out the time line.
clear FoundDayClock FoundDate FoundTime FoundHHn GnashDate;	%So cast off the assistants.

%                            Completed: clear my tracks.
clear damn idiot				%A large programme.
clear Toff iDate iTime iHHn iDayClock col	;%A few more.
fprintf(2,['So much for ',GnashFile,'\n']);	%Farewell.

%END. Just fall off the end, like.

function ShowStuff(cmax,col,style,Grist);	%Avoid some repetition.
 stylen{1} = 'qtext;'; stylen{2} = 'text; ';	%The joys of arrays in mutlub are thin.
 stylep{1} = 'date; '; stylep{2} = 'time; '; stylep{3} = 'datim;';	%Why can't I have stylename{-2:+3}?
 i2 = int2str(col);				%Misaligned columns
 if cmax >= 10 && col < 10, i2 = [' ',i2]; end;	%Look messy.
 I2 = int2str(style);				%I prefer alignment.
 if style < 0			%Surely there is a better way!
   blah = stylen{-style};		%And I can't have arrays bounded as I choose either!
  else				%Positive numbers have the leading space trimmed.
   I2 = [' ',I2];			%So put it back!
   if style > 0				%And I can't have an array with an element zero!
     blah = stylep{style};		%Only positive indices.
    else				%So,
     blah = 'grist;';			%Supply the zero value here.
   end;					%Not where the usage is defined.
 end;				%Curse mutlub!!
 disp(['  ColumnStyle(',i2,')=',I2,', ',blah,' has ',Grist]);	%Blah!
%END function ShowStuff.