kicad/eeschema/genliste.cpp

731 lines
20 KiB
C++

/***********************************************************/
/* genliste.cpp */
/* Module de generation de listing de composants, labels.. */
/***********************************************************/
#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"
#include "netlist.h"
#include "protos.h"
/* Structures pour memo et liste des elements */
typedef struct ListLabel
{
int Type();
void * m_Label;
int m_SheetNumber;
} ListLabel;
/* fonctions locales */
static void GenereListeOfItems(wxWindow * frame, const wxString & FullFileName);
static int GenListeGLabels( ListLabel * List );
static int ListTriComposantByRef(EDA_SchComponentStruct **Objet1,
EDA_SchComponentStruct **Objet2);
static int ListTriComposantByVal(EDA_SchComponentStruct **Objet1,
EDA_SchComponentStruct **Objet2);
static int ListTriGLabelBySheet(ListLabel *Objet1, ListLabel *Objet2);
static int ListTriGLabelByVal(ListLabel *Objet1, ListLabel *Objet2);
static void DeleteSubCmp( EDA_BaseStruct ** List , int NbItems);
static int PrintListeCmpByRef( FILE *f, EDA_BaseStruct **List, int NbItems);
static int PrintListeCmpByVal( FILE *f, EDA_BaseStruct **List, int NbItems);
static int PrintListeGLabel( FILE *f, ListLabel *List, int NbItems);
// Constantes:
#define LISTCMP_BY_REF 1
#define LISTCMP_BY_VAL 2
#define LIST_SUBCMP 4
#define LIST_HPINS_BY_NAME 8
#define LIST_HPINS_BY_SHEET 0x10
/* Variable locales */
int ItemsToList = LISTCMP_BY_REF | LISTCMP_BY_VAL;
enum id_tools {
ID_TOOLS_GEN_LIST = 1500,
ID_EXIT_TOOLS,
ID_SET_FILENAME_LIST
};
/* Classe de la frame de gestion de l'annotation */
class WinEDA_GenCmpListFrame: public wxDialog
{
public:
WinEDA_DrawFrame * m_Parent;
wxCheckBox * m_ListCmpbyRefItems;
wxCheckBox * m_ListCmpbyValItems;
wxCheckBox * m_ListSubCmpItems;
wxCheckBox * m_GenListLabelsbyVal;
wxCheckBox * m_GenListLabelsbySheet;
wxString m_LibArchiveFileName;
wxString m_ListFileName;
// Constructor and destructor
WinEDA_GenCmpListFrame(WinEDA_DrawFrame *parent, wxPoint& pos);
~WinEDA_GenCmpListFrame() {};
void GenList(wxCommandEvent& event);
void GenListUpdateOpt();
void ToolsExit(wxCommandEvent& event);
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(WinEDA_GenCmpListFrame, wxDialog)
EVT_BUTTON(ID_EXIT_TOOLS, WinEDA_GenCmpListFrame::ToolsExit)
EVT_BUTTON(ID_TOOLS_GEN_LIST, WinEDA_GenCmpListFrame::GenList)
END_EVENT_TABLE()
/***************************************************************/
void InstallToolsFrame(WinEDA_DrawFrame *parent, wxPoint & pos)
/***************************************************************/
{
WinEDA_GenCmpListFrame * frame = new WinEDA_GenCmpListFrame(parent, pos);
frame->ShowModal(); frame->Destroy();
}
/*****************************************************************************/
WinEDA_GenCmpListFrame::WinEDA_GenCmpListFrame(WinEDA_DrawFrame *parent,
wxPoint& framepos):
wxDialog(parent, -1, _("List of Material"), framepos, wxSize(360, 200),
DIALOG_STYLE)
/*****************************************************************************/
{
wxPoint pos;
m_Parent = parent;
SetFont(*g_DialogFont);
if ( (framepos.x == -1) && (framepos.x == -1) ) Centre();
/* Calcul du nom du fichier d'archivage */
m_LibArchiveFileName = MakeFileName(wxEmptyString,ScreenSch->m_FileName,wxEmptyString);
/* mise a jour extension */
ChangeFileNameExt(m_LibArchiveFileName, g_LibExtBuffer);
/* Calcul du nom du fichier de listage */
m_ListFileName = MakeFileName(wxEmptyString,ScreenSch->m_FileName,wxEmptyString);
/* mise a jour extension */
ChangeFileNameExt(m_ListFileName, wxT(".lst"));
pos.x = 5; pos.y = 15;
new wxStaticBox(this, -1,_(" List items: "), pos, wxSize(230, 120));
pos.x = 10; pos.y += 22;
m_ListCmpbyRefItems = new wxCheckBox(this,-1, _("Components by Reference"), pos);
if ( ItemsToList & LISTCMP_BY_REF ) m_ListCmpbyRefItems->SetValue(TRUE);
pos.y += 17;
m_ListCmpbyValItems = new wxCheckBox(this,-1, _("Components by Value"), pos);
if ( ItemsToList & LISTCMP_BY_VAL ) m_ListCmpbyValItems->SetValue(TRUE);
pos.y += 17;
m_ListSubCmpItems = new wxCheckBox(this,-1, _("Sub Components (i.e U2A, U2B..)"), pos);
if ( ItemsToList & LIST_SUBCMP ) m_ListSubCmpItems->SetValue(TRUE);
pos.y += 17;
m_GenListLabelsbyVal = new wxCheckBox(this,-1, _("Hierachy Pins by name"), pos);
if ( ItemsToList & LIST_HPINS_BY_NAME ) m_GenListLabelsbyVal->SetValue(TRUE);
pos.y += 17;
m_GenListLabelsbySheet = new wxCheckBox(this,-1, _("Hierachy Pins by Sheets"), pos);
if ( ItemsToList & LIST_HPINS_BY_SHEET ) m_GenListLabelsbySheet->SetValue(TRUE);
/* Creation des boutons de commande */
pos.x = 260; pos.y = 40;
wxButton * Button = new wxButton(this, ID_TOOLS_GEN_LIST,
_("&Create List"), pos);
Button->SetForegroundColour(*wxRED);
pos.y += Button->GetSize().y + 5;
Button = new wxButton(this, ID_EXIT_TOOLS,
_("&Exit"), pos);
Button->SetForegroundColour(*wxBLUE);
}
// Fonctions de positionnement des variables d'option
void WinEDA_GenCmpListFrame::GenListUpdateOpt()
{
ItemsToList = 0;
if ( m_ListCmpbyRefItems->GetValue() ) ItemsToList |= LISTCMP_BY_REF;
if ( m_ListCmpbyValItems->GetValue() ) ItemsToList |= LISTCMP_BY_VAL;
if ( m_ListSubCmpItems->GetValue() ) ItemsToList |= LIST_SUBCMP;
if ( m_GenListLabelsbyVal->GetValue() ) ItemsToList |= LIST_HPINS_BY_NAME;
if ( m_GenListLabelsbySheet->GetValue() ) ItemsToList |= LIST_HPINS_BY_SHEET;
}
void WinEDA_GenCmpListFrame::GenList(wxCommandEvent& event)
{
#define EXT_LIST wxT(".lst")
wxString mask, filename;
GenListUpdateOpt();
mask = wxT("*"); mask += EXT_LIST;
filename = EDA_FileSelector(_("List of material:"),
wxEmptyString, /* Chemin par defaut (ici dir courante) */
m_ListFileName, /* nom fichier par defaut, et resultat */
EXT_LIST, /* extension par defaut */
mask, /* Masque d'affichage */
this,
wxSAVE,
TRUE
);
if ( filename.IsEmpty() ) return;
else m_ListFileName = filename;
GenereListeOfItems(this, m_ListFileName);
Close();
}
void WinEDA_GenCmpListFrame::ToolsExit(wxCommandEvent& event)
{
GenListUpdateOpt();
Close();
}
/***************************************************************************/
static void GenereListeOfItems(wxWindow * frame, const wxString & FullFileName)
/***************************************************************************/
/*
Routine principale pour la creation des listings ( composants et/ou labels
globaux et "sheet labels" )
*/
{
FILE *f;
EDA_BaseStruct ** List;
ListLabel * ListOfLabels;
int NbItems;
char Line[1024];
wxString msg;
/* Creation de la liste des elements */
if ((f = wxFopen(FullFileName, wxT("wt"))) == NULL)
{
msg = _("Failed to open file "); msg << FullFileName;
DisplayError(frame, msg);
return;
}
NbItems = GenListeCmp(NULL );
if ( NbItems )
{
List = (EDA_BaseStruct **)
MyZMalloc( NbItems * sizeof(EDA_BaseStruct **) );
if (List == NULL )
{
fclose(f); return;
}
GenListeCmp(List);
/* generation du fichier listing */
DateAndTime(Line);
fprintf( f, "%s >> Creation date: %s\n", CONV_TO_UTF8(Main_Title), Line );
/* Tri et impression de la liste des composants */
qsort( List, NbItems, sizeof( EDA_BaseStruct * ),
(int(*)(const void*, const void*))ListTriComposantByRef);
if( (ItemsToList & LIST_SUBCMP) == 0 ) DeleteSubCmp(List, NbItems);
if( (ItemsToList & LISTCMP_BY_REF) )
{
PrintListeCmpByRef(f, List, NbItems);
}
if( (ItemsToList & LISTCMP_BY_VAL) )
{
qsort( List, NbItems, sizeof( EDA_BaseStruct * ),
(int(*)(const void*, const void*))ListTriComposantByVal);
PrintListeCmpByVal(f, List, NbItems);
}
MyFree( List );
}
/***************************************/
/* Generation liste des Labels globaux */
/***************************************/
NbItems = GenListeGLabels( NULL );
if ( NbItems )
{
ListOfLabels = (ListLabel *) MyZMalloc( NbItems * sizeof(ListLabel) );
if (ListOfLabels == NULL )
{
fclose(f); return;
}
GenListeGLabels(ListOfLabels);
/* Tri de la liste */
if( (ItemsToList & LIST_HPINS_BY_SHEET) )
{
qsort( ListOfLabels, NbItems, sizeof( ListLabel ),
(int(*)(const void*, const void*))ListTriGLabelBySheet);
msg = _("\n#Glob labels ( order = Sheet Number )\n");
fprintf( f, "%s", CONV_TO_UTF8(msg));
PrintListeGLabel(f, ListOfLabels, NbItems);
}
if( (ItemsToList & LIST_HPINS_BY_NAME) )
{
qsort( ListOfLabels, NbItems, sizeof( ListLabel ),
(int(*)(const void*, const void*))ListTriGLabelByVal);
msg = _("\n#Glob labels ( order = Alphab. )\n");
fprintf( f, "%s", CONV_TO_UTF8(msg));
PrintListeGLabel(f, ListOfLabels, NbItems);
}
MyFree( ListOfLabels );
}
msg = _("\n#End List\n");
fprintf( f, "%s", CONV_TO_UTF8(msg));
fclose(f);
}
/****************************************/
int GenListeCmp( EDA_BaseStruct ** List )
/****************************************/
/* Routine de generation de la liste des elements utiles du dessin
Si List == NULL: comptage des elements
Sinon remplissage de la liste
Initialise "FlagControlMulti" a SheetNumber pour la sortie des listes
et m_Father comme pointeur sur la sheet d'appartenance
*/
{
int ii = 0;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
BASE_SCREEN * screen = ScreenSch;
for( ; screen != NULL ; screen = (BASE_SCREEN*)screen->Pnext )
{
DrawList = screen->EEDrawList;
while ( DrawList )
{
switch( DrawList->Type() )
{
case DRAW_LIB_ITEM_STRUCT_TYPE :
ii++;
DrawLibItem = (EDA_SchComponentStruct *) DrawList;
DrawLibItem->m_FlagControlMulti = screen->m_SheetNumber;
DrawLibItem->m_Parent = screen;
if( List )
{
*List = DrawList; List++;
}
break;
default: break;
}
DrawList = DrawList->Pnext;
}
}
return ( ii );
}
/*********************************************/
static int GenListeGLabels( ListLabel * List )
/*********************************************/
/* Count the Glabels, or fill the list Listwith Glabel pointers
If List == NULL: Item count only
Else fill list of Glabels
*/
{
int ii = 0;
EDA_BaseStruct *DrawList;
DrawSheetLabelStruct *SheetLabel;
BASE_SCREEN * screen = ScreenSch;
for( ; screen != NULL ; screen = (BASE_SCREEN*)screen->Pnext )
{
DrawList = screen->EEDrawList;
while ( DrawList )
{
switch( DrawList->Type() )
{
case DRAW_GLOBAL_LABEL_STRUCT_TYPE :
if( List )
{
List->Type() = DRAW_TEXT_STRUCT_TYPE;
List->m_SheetNumber = screen->m_SheetNumber;
List->m_Label = DrawList; List++;
}
ii++;
break;
case DRAW_SHEET_STRUCT_TYPE :
{
#define Sheet ((DrawSheetStruct * ) DrawList)
SheetLabel= Sheet->m_Label;
while( SheetLabel != NULL )
{
if ( List )
{
List->Type() = DRAW_SHEETLABEL_STRUCT_TYPE;
List->m_SheetNumber = screen->m_SheetNumber;
List->m_Label = SheetLabel;
List++;
}
ii++;
SheetLabel = (DrawSheetLabelStruct*)(SheetLabel->Pnext);
}
break;
}
default: break;
}
DrawList = DrawList->Pnext;
}
}
return ( ii );
}
/**********************************************************/
static int ListTriComposantByVal(EDA_SchComponentStruct **Objet1,
EDA_SchComponentStruct **Objet2)
/**********************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
Les composants sont tries
par valeur
si meme valeur: par reference
si meme valeur: par numero d'unite
*/
{
int ii;
const wxString * Text1, *Text2;
if( (*Objet1 == NULL) && (*Objet2 == NULL ) ) return(0);
if( *Objet1 == NULL) return(-1);
if( *Objet2 == NULL) return(1);
Text1 = &(*Objet1)->m_Field[VALUE].m_Text;
Text2 = &(*Objet2)->m_Field[VALUE].m_Text;
ii = Text1->CmpNoCase(*Text2);
if( ii == 0 )
{
Text1 = &(*Objet1)->m_Field[REFERENCE].m_Text;
Text2 = &(*Objet2)->m_Field[REFERENCE].m_Text;
ii = Text1->CmpNoCase(*Text2);
}
if ( ii == 0 )
{
ii = (*Objet1)->m_Multi - (*Objet2)->m_Multi;
}
return(ii);
}
/**********************************************************/
static int ListTriComposantByRef(EDA_SchComponentStruct **Objet1,
EDA_SchComponentStruct **Objet2)
/**********************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
Les composants sont tries
par reference
si meme referenece: par valeur
si meme valeur: par numero d'unite
*/
{
int ii;
const wxString * Text1, *Text2;
if( (*Objet1 == NULL) && (*Objet2 == NULL ) ) return(0);
if( *Objet1 == NULL) return(-1);
if( *Objet2 == NULL) return(1);
Text1 = &(*Objet1)->m_Field[REFERENCE].m_Text;
Text2 = &(*Objet2)->m_Field[REFERENCE].m_Text;
ii = Text1->CmpNoCase(*Text2);
if( ii == 0 )
{
Text1 = &(*Objet1)->m_Field[VALUE].m_Text;
Text2 = &(*Objet2)->m_Field[VALUE].m_Text;
ii = Text1->CmpNoCase(*Text2);
}
if ( ii == 0 )
{
ii = (*Objet1)->m_Multi - (*Objet2)->m_Multi;
}
return(ii);
}
/******************************************************************/
static int ListTriGLabelByVal(ListLabel *Objet1, ListLabel *Objet2)
/*******************************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
Les labels sont tries
par comparaison ascii
si meme valeur: par numero de sheet
*/
{
int ii;
const wxString * Text1, *Text2;
if( Objet1->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
Text1 = &((DrawSheetLabelStruct *)Objet1->m_Label)->m_Text;
else
Text1 = &((DrawTextStruct *)Objet1->m_Label)->m_Text;
if( Objet2->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
Text2 = &((DrawSheetLabelStruct *)Objet2->m_Label)->m_Text;
else
Text2 = &((DrawTextStruct *)Objet2->m_Label)->m_Text;
ii = Text1->CmpNoCase(*Text2);
if ( ii == 0 )
{
ii = Objet1->m_SheetNumber - Objet2->m_SheetNumber;
}
return(ii);
}
/*******************************************************************/
static int ListTriGLabelBySheet(ListLabel *Objet1, ListLabel *Objet2)
/*******************************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
Les labels sont tries
par sheet number
si meme valeur, par ordre alphabetique
*/
{
int ii;
const wxString * Text1, *Text2;
ii = Objet1->m_SheetNumber - Objet2->m_SheetNumber;
if ( ii == 0 )
{
if( Objet1->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
Text1 = &((DrawSheetLabelStruct *)Objet1->m_Label)->m_Text;
else
Text1 = &((DrawTextStruct *)Objet1->m_Label)->m_Text;
if( Objet2->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
Text2 = &((DrawSheetLabelStruct *)Objet2->m_Label)->m_Text;
else
Text2 = &((DrawTextStruct *)Objet2->m_Label)->m_Text;
ii = Text1->CmpNoCase(*Text2);
}
return(ii);
}
/**************************************************************/
static void DeleteSubCmp( EDA_BaseStruct ** List, int NbItems )
/**************************************************************/
/* Supprime les sous-composants, c'est a dire les descriptions redonnantes des
boitiers multiples
La liste des composant doit etre triee par reference et par num d'unite
*/
{
int ii;
EDA_SchComponentStruct * LibItem;
const wxString * OldName = NULL;
for( ii = 0; ii < NbItems ; ii++ )
{
LibItem = (EDA_SchComponentStruct *) List[ii];
if ( LibItem == NULL ) continue;
if( OldName )
{
if ( OldName->CmpNoCase( LibItem->m_Field[REFERENCE].m_Text ) == 0 )
{
List[ii] = NULL;
}
}
OldName = &LibItem->m_Field[REFERENCE].m_Text;
}
}
/**********************************************************************/
int PrintListeCmpByRef( FILE * f, EDA_BaseStruct ** List, int NbItems )
/**********************************************************************/
/* Impression de la liste des composants tries par reference
*/
{
int ii, Multi, Unit;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
EDA_LibComponentStruct *Entry;
char NameCmp[80];
wxString msg;
msg = _("\n#Cmp ( order = Reference )");
if ( (ItemsToList & LIST_SUBCMP) ) msg << _(" (with SubCmp)");
fprintf( f, "%s\n", CONV_TO_UTF8(msg));
for ( ii = 0; ii < NbItems; ii++ )
{
DrawList = List[ii];
if( DrawList == NULL ) continue;
if( DrawList->Type() != DRAW_LIB_ITEM_STRUCT_TYPE ) continue;
DrawLibItem = (EDA_SchComponentStruct *) DrawList;
if( DrawLibItem->m_Field[REFERENCE].m_Text[0] == '#' ) continue;
Multi = 0; Unit = ' ';
Entry = FindLibPart(DrawLibItem->m_ChipName.GetData(), wxEmptyString, FIND_ROOT);
if( Entry ) Multi = Entry->m_UnitCount;
if( (Multi > 1 ) && ( ItemsToList & LIST_SUBCMP ) )
Unit = DrawLibItem->m_Multi + 'A' - 1;
sprintf( NameCmp,"%s%c", CONV_TO_UTF8(DrawLibItem->m_Field[REFERENCE].m_Text),
Unit);
fprintf(f, "| %-10.10s %-12.12s",
NameCmp,
CONV_TO_UTF8(DrawLibItem->m_Field[VALUE].m_Text));
if ( (ItemsToList & LIST_SUBCMP) )
{
BASE_SCREEN * screen = (BASE_SCREEN *)(DrawLibItem->m_Parent);
wxString sheetname;
if( screen->m_Parent )
sheetname = ((DrawSheetStruct*)screen->m_Parent)->m_Field[VALUE].m_Text.GetData();
else sheetname = _("Root");
fprintf(f, " (Sheet %.2d: \"%s\")", DrawLibItem->m_FlagControlMulti,
CONV_TO_UTF8(sheetname));
}
fprintf(f,"\n");
}
msg = _("#End Cmp\n");
fprintf(f, CONV_TO_UTF8(msg));
return(0);
}
/***********************************************************************/
int PrintListeCmpByVal( FILE * f, EDA_BaseStruct ** List, int NbItems )
/**********************************************************************/
{
int ii, Multi;
wxChar Unit;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
EDA_LibComponentStruct *Entry;
wxString msg;
msg = _("\n#Cmp ( order = Value )");
if ( (ItemsToList & LIST_SUBCMP) ) msg << _(" (with SubCmp)");
msg << wxT("\n");
fprintf(f, CONV_TO_UTF8(msg));
for ( ii = 0; ii < NbItems; ii++ )
{
DrawList = List[ii];
if( DrawList == NULL ) continue;
if( DrawList->Type() != DRAW_LIB_ITEM_STRUCT_TYPE ) continue;
DrawLibItem = (EDA_SchComponentStruct *) DrawList;
if( DrawLibItem->m_Field[REFERENCE].m_Text[0] == '#' ) continue;
Multi = 0; Unit = ' ';
Entry = FindLibPart(DrawLibItem->m_ChipName.GetData(), wxEmptyString, FIND_ROOT);
if( Entry ) Multi = Entry->m_UnitCount;
if( (Multi > 1 ) && ( ItemsToList & LIST_SUBCMP ) )
Unit = DrawLibItem->m_Multi + 'A' - 1;
msg = DrawLibItem->m_Field[REFERENCE].m_Text;
msg.Append(Unit);
fprintf(f, "| %-12.12s %-10.10s",
CONV_TO_UTF8(DrawLibItem->m_Field[VALUE].m_Text),
CONV_TO_UTF8(msg) );
if ( (ItemsToList & LIST_SUBCMP) )
{
fprintf(f, " (Sheet %.2d)", DrawLibItem->m_FlagControlMulti);
}
fprintf(f,"\n");
}
msg = _("#End Cmp\n");
fprintf(f, CONV_TO_UTF8(msg));
return(0);
}
/******************************************************************/
static int PrintListeGLabel( FILE *f, ListLabel *List, int NbItems)
/******************************************************************/
{
int ii, jj;
DrawGlobalLabelStruct *DrawTextItem;
DrawSheetLabelStruct * DrawSheetLabel;
ListLabel * LabelItem;
wxString msg;
for ( ii = 0; ii < NbItems; ii++ )
{
LabelItem = & List[ii];
switch( LabelItem->Type() )
{
case DRAW_GLOBAL_LABEL_STRUCT_TYPE :
DrawTextItem = (DrawGlobalLabelStruct *)(LabelItem->m_Label);
msg.Printf(
_("> %-28.28s Global (Sheet %.2d) pos: %3.3f, %3.3f\n"),
DrawTextItem->m_Text.GetData(),
LabelItem->m_SheetNumber,
(float)DrawTextItem->m_Pos.x / 1000,
(float)DrawTextItem->m_Pos.y / 1000);
fprintf(f, CONV_TO_UTF8(msg));
break;
case DRAW_SHEETLABEL_STRUCT_TYPE :
{
DrawSheetLabel = (DrawSheetLabelStruct *) LabelItem->m_Label;
jj = DrawSheetLabel->m_Shape;
if ( jj < 0 ) jj = NET_TMAX; if ( jj > NET_TMAX ) jj = 4;
wxString labtype = CONV_FROM_UTF8(SheetLabelType[jj]);
msg.Printf(
_("> %-28.28s Sheet %-7.7s (Sheet %.2d) pos: %3.3f, %3.3f\n"),
DrawSheetLabel->m_Text.GetData(),
labtype.GetData(),
LabelItem->m_SheetNumber,
(float)DrawSheetLabel->m_Pos.x / 1000,
(float)DrawSheetLabel->m_Pos.y / 1000);
fprintf(f, CONV_TO_UTF8(msg));
}
break;
default: break;
}
}
msg = _("#End labels\n");
fprintf(f, CONV_TO_UTF8(msg));
return(0);
}