Le web de Dominique Guebey – Bazar informatique

Page : http://www.dg77.net/tekno/manuel/rexxrss2.htm


  D o m i n i q u e   G u e b e y    J u n g l e    Bazar informatique

REXX : génération spécifique de fichier RSS

À noter
Principe
Il s’agit de récupérer et ordonner aussi bien que possible des résultats sportifs d’origines diverses. Voir le programme sommaire
Mise en conformité
Certains caractères ou groupes de caractères sont adaptés : &, <, ainsi que le double tiret (interdit en XML). Voir la routine NORMAL dans le programme ci-dessous.
Fonctionnalités supplémentaires
Alignement des chronos en 2e colonne. Les temps sont repérés en positionnant avant eux les caractères “\$”.
Alignement des affiliations (ou autre, année de naissance etc.) en position 50. Cette information est repérée en positionnant devant les caractères “\#”.
Pour les résultats français, alignement des catégories (codes du site de la Fédération Française d’Athlétisme) en colonne 80. Voir la routine CATEG.
Alignement supplémentaire possible en position 90, en intercalant les caractères “\%”.
Dans le titre :
  • Dans les résultats F.F.A., le code région est placé automatiquement au début, avec un balisage spécial. Voir la routine REGION.
  • Ibidem pour le code pays dans les autres résultats (routine PAYS).
  • Résultats F.F.A. : le titre peut être constitué à partir des deux premières lignes (cf options en entrée du programme).
Traîtement de l’encodage
Les données lues sont en UTF-8, qui est un encodage de longueur variable. Les caractères accentués et certaines langues y occupent deux octets voire plus. C’est un sérieux inconvénient quand on veut compter les caractères pour aligner le texte en colonnes séparées : le nombre d’octets lus par le programme diffère de ce que le lecteur humain voit. Pour contourner le problème, les données son converties en UNICODE. Voir notre page Le codage des caractères.
Exemples

Remarques :

La source d’origine :

.
NED Tilburg 1 oct 2016, Championnats Hollande/Belgique, 9e 50km
** 50km M
1 Rob Tersteeg \#76 NED RWV \$4:42:06 1
2 Wilfried Van Bremen \#87 NED RWV \$5:22:41 2
3 Jack Koolen \#69 NED Unitas \$5:33:38 3
4 René Wakkee \#65 NED RWV \$5:39:11 4
5 Andrea Toth (F) \#71 HUN Honved \$5:47:00
** 20km (Champ. Bel 20km W)
1 Rick Liesting \#77 NED OLAT \$1:37:58
2 Remco de Bruin \#64 NED LAT \$1:44:15
3 Annelies Sarrazin (F) \#81 BEL FLAC \$1:55:56 1
4 Myriam Nicolas (F) \#63 BEL SMAC \$2:00:58 2
http://www.tigch.nl/snelwandelen/index_2016.htm

.
JPN Nomi, 19 mar 2017, Asian 20km Championships,
** 20km M
 1 MATSUNAGA DAISUKE 松永 大介マツナガ ダイスケ \#4 神奈川・東洋大学\%19:24 39:18 (19:54) 59:25 (20:07) \$1:19:40 (20:15)
 2 KIM, Hyunsub キム・フンスブ \#KOR・韓国\%19:43 39:34 (19:51) 59:40 (20:06) \$1:19:50 (20:10) 0:10
 3 NODA TOMOHIRO 野田 明宏ノダ トモヒロ \#3 大阪・明治大学\%19:51 39:43 (19:52) 59:52 (20:09) \$1:20:04 (20:12) 0:24
 4 SHEIKO, Georgiy ゲオルギー・シェイコ \#KAZ・カザフスタン\%19:54 40:04 (20:10) 1:00:27 (20:23) \$1:20:47 (20:20) 1:07
 5 TAKUMI SAITO 西塔 拓己サイトウ  \#タクミ愛知・愛知製鋼\%19:52 39:48 (19:56) 59:54 (20:06) \$1:20:56 (21:02) 1:16
 6 KOLOTHUM THODI, Irfan イルファン・コロトゥム トージ \#IND・インド\%19:51 39:55 (20:04) 1:00:28 (20:33) \$1:20:59 (20:31) 1:19
 7 OIKAWA FUMITAKA 及川 文隆オイカワ フミタカ \#3 愛知県・東洋大学\%19:51 39:48 (19:57) 1:00:13 (20:25) \$1:21:08 (20:55) 1:28
 8 YAMANISHI Toshikazu ヤマニシ・トシカズ \#JPN・日本\%19:34 39:34 (20:00) 1:00:03 (20:29) \$1:21:23 (21:20) 1:43
** 20km W
 1 WANG Na ワン・ナ \#CHN・中国\%22:41 44:56 (22:15) 1:07:40 (22:44) \$1:30:51 (23:11)
 2 ASADA CHIAKI 淺田  \#千安芸アサダ チアキ福岡・DNP西日本\%22:56 45:51 (22:55) 1:09:03 (23:12) \$1:32:12 (23:09) 1:21
 3 YOSHIZUMI YUKI 吉住 友希ヨシズミ ユキ \#3 千葉・千葉県立保健医療大学\%22:56 45:51 (22:55) 1:09:03 (23:12) \$1:32:23 (23:20) 1:32
 4 KAWAZOE KAORI 河添 香織カワゾエ カオリ \#3 京都・立命館大学\%22:48 45:40 (22:52) 1:09:04 (23:24) \$1:33:09 (24:05) 2:18
 5 OKADA Kumiko オカダ・クミコ \#JPN・日本\%22:48 45:39 (22:51) 1:09:03 (23:24) \$1:33:21 (24:18) 2:30
 6 YANG, Peili ヤン・ペイリ \#CHN・中国\%22:56 45:52 (22:56) 1:09:26 (23:34) \$1:34:18 (24:52) 3:27
DNF JEON, Yeongeun ジョン・ヨンクン \#KOR・韓国\%22:48 45:51 (23:03) 1:09:26 (23:35)
DSQ RYKOVA, Regina レジーナ・リコワ \#KAZ・カザフスタン\%23:30 47:13 (23:43) 1:11:08 (23:55) \$1:35:45 (24:37) 4:54
http://www.jaaf.or.jp/taikai/1395/

16/10/16 - Championnats Promotion Interclubs Cadets/Juniors
BRIVE LA GAILLARDE - LIM
** 3000m / TCF 9:51
1 14'17"04 TERREC Eloise \#Ac Roche-sur-yon JUF/98
2 16'33"36 MANARESI Marion \#Ac Romorantin CAF/00
3 16'55"12 LARROZE-LAUGA Pauline \#Adour Pyrenees Athletisme 65 CAF/00
4 17'19"68 PEDROT Mylene \#Athletisme Sud 22 CAF/00
5 17'29"95 THRO Capucine \#Fac Andrezieux JUF/98
6 19'55"56 NOEL Emilie \#Ea Bourg-en-bresse CAF/00
** 5000m / TCM 9:13
1 23'15"52 HALOUA BOUKHRIS Zineddine (ESP) \#Athle 66 JUM/98
2 23'56"81 BRACHET Thomas \#Afa Feyzin-venissieux JUM/98
3 24'12"72 GRELLIER Hugo \#Ac Roche-sur-yon CAM/99
4 26'54"07 VERDIERE Baptiste \#Stade Lavallois CAM/00
http://bases.athle.com/asp.net/liste.aspx?frmbase=resultats&frmmode=1&frmespace=0&frmcompetition=189816

Le fichier obtenu :

Remarque : le décallage des colonnes en caractères japonais est dû au fait qu’on n’utilise pas une police de type « monospace ».

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
xmlns="http://blogs.law.harvard.edu/tech/rss"
xmlns:dg="http://ns.dg77.net/XML/" 
xmlns:d="http://ns.dg77.net/XML/d/" 
>
<dg:description>
   <dg:cre></dg:cre>
   <dg:upd></dg:upd>
</dg:description>
<channel>
   <title></title>
   <description></description>
   <link></link>
   <lastBuildDate></lastBuildDate>


<item>
  <guid isPermaLink="false">world-617</guid>
  <title><dg:span class="monosp">NED</dg:span> Tilburg 1 oct 2016, Championnats Hollande/Belgique, 9e 50km</title>
   <description dg:format="pre">** 50km M
  1 4:42:06   Rob Tersteeg                      76 NED RWV  1
  2 5:22:41   Wilfried Van Bremen               87 NED RWV  2
  3 5:33:38   Jack Koolen                       69 NED Unitas  3
  4 5:39:11   René Wakkee                       65 NED RWV  4
  5 5:47:00   Andrea Toth (F)                   71 HUN Honved
** 20km (Champ. Bel 20km W)
  1 1:37:58   Rick Liesting                     77 NED OLAT
  2 1:44:15   Remco de Bruin                    64 NED LAT
  3 1:55:56   Annelies Sarrazin (F)             81 BEL FLAC  1
  4 2:00:58   Myriam Nicolas (F)                63 BEL SMAC  2
</description>
<link>http://www.tigch.nl/snelwandelen/index_2016.htm</link>
<pubDate></pubDate></item>

<item>
  <guid isPermaLink="false">world-618</guid>
  <title><dg:span class="monosp">JPN</dg:span> Nomi, 19 mar 2017, Asian 20km Championships, </title>
   <description dg:format="pre">** 20km M
  1 1:19:40   MATSUNAGA DAISUKE 松永 大介マツナガ ダイスケ 4 神奈川・東洋大学                             19:24 39:18 (19:54) 59:25 (20:07)  (20:15)
  2 1:19:50   KIM, Hyunsub キム・フンスブ             KOR・韓国                                  19:43 39:34 (19:51) 59:40 (20:06)  (20:10) 0:10
  3 1:20:04   NODA TOMOHIRO 野田 明宏ノダ トモヒロ       3 大阪・明治大学                               19:51 39:43 (19:52) 59:52 (20:09)  (20:12) 0:24
  4 1:20:47   SHEIKO, Georgiy ゲオルギー・シェイコ      KAZ・カザフスタン                              19:54 40:04 (20:10) 1:00:27 (20:23)  (20:20) 1:07
  5 1:20:56   TAKUMI SAITO 西塔 拓己サイトウ            タクミ愛知・愛知製鋼                              19:52 39:48 (19:56) 59:54 (20:06)  (21:02) 1:16
  6 1:20:59   KOLOTHUM THODI, Irfan イルファン・コロトゥム トージ IND・インド                            19:51 39:55 (20:04) 1:00:28 (20:33)  (20:31) 1:19
  7 1:21:08   OIKAWA FUMITAKA 及川 文隆オイカワ フミタカ    3 愛知県・東洋大学                              19:51 39:48 (19:57) 1:00:13 (20:25)  (20:55) 1:28
  8 1:21:23   YAMANISHI Toshikazu ヤマニシ・トシカズ    JPN・日本                                  19:34 39:34 (20:00) 1:00:03 (20:29)  (21:20) 1:43
** 20km W
  1 1:30:51   WANG Na ワン・ナ                      CHN・中国                                  22:41 44:56 (22:15) 1:07:40 (22:44)  (23:11)
  2 1:32:12   ASADA CHIAKI 淺田                   千安芸アサダ チアキ福岡・DNP西日本                    22:56 45:51 (22:55) 1:09:03 (23:12)  (23:09) 1:21
  3 1:32:23   YOSHIZUMI YUKI 吉住 友希ヨシズミ ユキ      3 千葉・千葉県立保健医療大学                         22:56 45:51 (22:55) 1:09:03 (23:12)  (23:20) 1:32
  4 1:33:09   KAWAZOE KAORI 河添 香織カワゾエ カオリ      3 京都・立命館大学                              22:48 45:40 (22:52) 1:09:04 (23:24)  (24:05) 2:18
  5 1:33:21   OKADA Kumiko オカダ・クミコ             JPN・日本                                  22:48 45:39 (22:51) 1:09:03 (23:24)  (24:18) 2:30
  6 1:34:18   YANG, Peili ヤン・ペイリ               CHN・中国                                  22:56 45:52 (22:56) 1:09:26 (23:34)  (24:52) 3:27
 NF         JEON, Yeongeun ジョン・ヨンクン            KOR・韓国                                  22:48 45:51 (23:03) 1:09:26 (23:35)
 DQ 1:35:45 RYKOVA, Regina レジーナ・リコワ            KAZ・カザフスタン                              23:30 47:13 (23:43) 1:11:08 (23:55)  (24:37) 4:54
</description>
<link>http://www.jaaf.or.jp/taikai/1395/</link>
<pubDate></pubDate></item>

<item>
  <guid isPermaLink="false">test-00619</guid>
  <title><dg:span class="monosp">LIM</dg:span> BRIVE LA GAILLARDE - 16/10/16 - Championnats Promotion Interclubs Cadets/Juniors</title>
   <description dg:format="pre">** 3000m / TCF 9:51
  1  14'17"04 TERREC Eloise                     Ac Roche-sur-yon                      JUF/98
  2  16'33"36 MANARESI Marion                   Ac Romorantin                         CAF/00
  3  16'55"12 LARROZE-LAUGA Pauline             Adour Pyrenees Athletisme 65          CAF/00
  4  17'19"68 PEDROT Mylene                     Athletisme Sud 22                     CAF/00
  5  17'29"95 THRO Capucine                     Fac Andrezieux                        JUF/98
  6  19'55"56 NOEL Emilie                       Ea Bourg-en-bresse                    CAF/00
** 5000m / TCM 9:13
  1  23'15"52 HALOUA BOUKHRIS Zineddine (ESP)   Athle 66                              JUM/98
  2  23'56"81 BRACHET Thomas                    Afa Feyzin-venissieux                 JUM/98
  3  24'12"72 GRELLIER Hugo                     Ac Roche-sur-yon                      CAM/99
  4  26'54"07 VERDIERE Baptiste                 Stade Lavallois                       CAM/00
</description>
<link>http://bases.athle.com/asp.net/liste.aspx?frmbase=resultats&amp;frmmode=1&amp;frmespace=0&amp;frmcompetition=189816&lt;/link>
<pubDate></pubDate></item>

</channel>
</rss>

Le programme

Remarques :

/* REXX - Ce programme dispose le contenu d'un fichier texte dans un fichier RSS 2.0
 Cf ~/tekno/manuel/rexxrss2.htm
 Ce programme est censé lire un fichier encodé en UTF-8
 Voir autres commentaires à la fin 
*/

/* INITIALISATIONS //////////////////////////////////////////////// */
parse source src.1 src.2 src.3 /* Monitorage des erreurs */
SIGNAL ON ERROR  NAME erreur
SIGNAL ON HALT NAME erreur
SIGNAL ON SYNTAX NAME erreur

OKF = 0         /* 1=au moins une ligne non vide  */
CALL INITEM

endian = '' ; blanx = ''
CALL testendian.rex /* Module externe */
PARSE VAR RESULT endian blanx /* récupère un caractère espace hexadecimal UNICODE */

/* ENTREE DES PARAMETRES //////////////////////////////////// */
parse arg fentree fsortie prefixe numero ptitr
IF fentree == "" then do 
  say " Fichier lu ? " ; parse pull fentree /* parse : pour garder les caractères minuscules */
  end
IF fentree == "" then do
  say 'Aucun nom de fichier d''entree'
  say 'No input file name'
  pull
  exit
  end
IF LINES(fentree) == 0 then do
  say 'Le fichier d''entree ' fentree ' est vide ou inexistant'
  say 'Input file ' fentree 'is empty or nonexistent'
  pull
  exit
  end

IF fsortie == "" then do 
  say " Fichier sorti ? " ; parse pull fsortie
  end
IF fsortie == "" THEN exit
IF prefixe == "" then do 
  say " Prefixe ? " ; parse pull prefixe
  end
  if prefixe == "" | DATATYPE(prefixe, N) = 1 then do
    zontrav = "D-" || prefixe
    prefixe = zontrav
    end
    
/* Compteur mis dans un enregistrement (lecture) */
IF (numero == "") | (DATATYPE(numero, N) = 1)  THEN DO
  f_num = linein('marche/news/news_src/news_num.txt')
  PARSE VAR f_num fnum .
  if DATATYPE(fnum, N) then numero = fnum
  say 'Precedent no utilise / previous number : ' numero

IF numero == "" then do 
  say " Premier numero a utiliser ? " ; pull numero
  end
if (numero == "") | (DATATYPE(numero, N) = 0) THEN numero = 1
IF ptitr == "" then do 
  say " Titre sur 2 lignes O/(n=defaut) ? " ; pull ptitr /* pull : convertit en majuscules */
  end

/* BOUCLE DE LECTURE DU FICHIER //////////////////////////////////// */
DO while lines(fentree)>0
    lig_ent = linein(fentree)
    nonblanc = wordindex(lig_ent,1)
    if nonblanc = 0 then do /* Ligne vide */
        if ITEM = 1 THEN CALL FINITM 
        end
    ELSE if wordindex(ligne,1) > 0 THEN CALL lig_trait
    ligne = lig_ent
end
/* FIN DE FICHIER ATTEINTE  */
IF ITEM = 1 THEN CALL FINITM
IF OKF = 1 then do
    CALL lineout fsortie, '</channel>'
    CALL lineout fsortie, '</rss>'
    end

/* Compteur mis dans un enregistrement (sortie) */
CALL lineout 'marche/news/news_src/news_num.txt', numero, 1
say 'Dernier numero / last number : ' numero
    
EXIT /*                             FIN DU PROGRAMME */

/* //////////////////////////////////////////////////////////////////// */

/* TRAITEMENT D'UNE LIGNE (titre ou detail) */
LIG_TRAIT:

IF OKF=0 THEN CALL DEB /* 1ere sortie : mettre les entetes */

IF ITEM = 0 then do
    CALL lineout fsortie, '<item>'
    ITEM = 1
    end

IF (ptitr = 'O' & TITR = 1) | (ptitr \= 'O' & TITR = 0) then do /* L2 */
    /* Debut de l'item        */
    if numero \= 0 then do
        CALL lineout fsortie, '  <guid isPermaLink="false">'prefixe || numero'</guid>'
        numero = numero + 1
        end
    if ptitr = 'O' then do /* Titre avec 2 premieres lignes */
        CALL REGION
        CALL PAYS
        CALL NET
        ltitre = ligne || ZTITRE
        CALL lineout fsortie, '<title>'ltitre'</title>'
        end
    else do /* Titre dans la premiere ligne */
        CALL PAYS
        CALL NET
        CALL lineout fsortie, '  <title>'ligne'</title>'
        end
    TITR = 2
    end
else do  /* L2 */
    if ptitr = 'O' & TITR=0 then do /* L3 */
        CALL NORMAL
        if ligne \== '.' THEN ZTITRE = ' ' || ligne
        TITR = 1
        end
    else do /* L3 - Apres le titre : on sort les lignes de la partie description  */
        if DESCR = 0 THEN CALL charout fsortie, '   <description dg:format="pre">' 
        DESCR = 1
        CALL NORMAL
        if substr(ligne,1,1) \=="*" then do
           /* Mise en 2e colonne des données précédées par "\$" */
           posl = POS('\$',ligne)                                            
           if posl > 0 then do                                               
              long = LENGTH(ligne)
              zsep = WORDINDEX(ligne, 2) -1 
              if zsep > 0 then do
                 poslf = POS(' ',ligne,posl)
                 trav2 = SUBSTR(ligne,1,zsep-1)
                 if poslf == 0 THEN trav2 = trav2 || ' ' || SUBSTR(ligne,posl+2,long-posl-1)
                               ELSE trav2 = trav2 || ' ' || SUBSTR(ligne,posl+2,poslf-posl-2)
                 trav2 = trav2 || ' ' || SUBSTR(ligne,zsep+1,posl-zsep-1) || ' '
                 if poslf > 0 THEN trav2 = trav2 || SUBSTR(ligne,poslf+1,long-poslf)
                 ligne = trav2
              end
           end
           parse var ligne zon1 suite
           zontrav = zon1
           ltrav = LENGTH(zon1)
           if ltrav > 1 & SUBSTR(zon1,ltrav) == '.' THEN zontrav = SUBSTR(zon1, 1, ltrav - 1)
           if DATATYPE(zontrav, N) = 1 then do /* 1ere colonne est un no classement */
              if zontrav > 0 & zontrav < 10    THEN ligne = '  ' || zontrav || ' ' || suite
              if zontrav > 9 & zontrav < 100   THEN ligne = ' ' || zontrav || ' ' || suite
              if zontrav > 99 & zontrav < 1000 THEN ligne = zontrav || ' ' || suite
              end
           else do /* Cas ou la 1ere colonne n'indique pas le classement */
              if DATATYPE(SUBSTR(zon1,1,1), N) = 0 then do
                  if zon1 == 'DSQ' THEN zon1 = 'DQ'
                  if zon1 == 'DNF' THEN zon1 = 'NF'
                  if zon1 == 'DQ' | zon1 == 'NF' | zon1 == 'AB' | zon1 == 'NS' | ,
                     zon1 == 'NP' | zon1 == 'NC' | zon1 == 'HC' | zon1 == 'HM' ,
                     then do
                        ligne = ' ' || zon1 || ' ' || suite
                        trav = WORDINDEX(suite, 1)
                        if trav > 0 then do
                           if DATATYPE(SUBSTR(suite, trav, 1),N) = 0 then do
                              cold = WORDINDEX(ligne, 2)
                              if cold > 0 then CALL DECAL16 /* Aligne en position 16 la 2e colonne */
                              end
                           end
                        end
                  end
              end
           cold = WORDINDEX(ligne, 3) /* Aligne en position 16 la 3e colonne */
           col2 = WORDINDEX(ligne, 2)
           if cold > 0 then do                                 
             if DATATYPE(SUBSTR(ligne, col2, 1),N) = 0 then do 
                PARSE VAR ligne . z2 .                         
                if z2 == 'NC' | z2 == '*hb*' then call DECAL16 
                end                                            
             else CALL DECAL16                                 
           end                                                 
        end

        /* Decalage en position 49 du 1er car après "\#" */
        CALL ALIGNE '\#', 50, ligne
        CALL REMPLACE '\#', ''
        /* Decalage en position 79 du 1er car après "\%" */
        CALL ALIGNE '\%', 80, ligne
        CALL REMPLACE '\%', ''

        /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
        posisep = 0
        if substr(ligne,1) \== "*" THEN CALL CATEG /* Aligne categories FFA */

        CALL lineout fsortie, STRIP(ligne,T) /* strip ,t supprime les blancs de fin */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
    end  /* L3 */
end /* L2 */
RETURN

/* ///////////////////////////// ROUTINES /////////////////////////////// */
DECAL16: /* - - - - - - - - - - - - - - - - - - */
if cold < 16 then do
 sep = 17 - cold
 tampon = ''
 if sep > 0 then do sep
  tampon = tampon || '20'x
  end
 zontrav = SUBSTR(ligne, 1, cold-2) || tampon || SUBSTR(ligne, cold)
 ligne = zontrav
 end
RETURN

DEB: /* - - - - - - - - - - - - - - - - - - */
OKF = 1
/*    - Sort Tags de debut                                     */
CALL lineout fsortie, '<?xml version="1.0" encoding="utf-8"?>'
CALL lineout fsortie, '<rss version="2.0" '
CALL lineout fsortie, 'xmlns="http://blogs.law.harvard.edu/tech/rss"'
CALL lineout fsortie, 'xmlns:dg="http://ns.dg77.net/XML/" '
CALL lineout fsortie, 'xmlns:d="http://ns.dg77.net/XML/d/" '
CALL lineout fsortie, '>'
CALL lineout fsortie, '<dg:description>'
CALL lineout fsortie, '   <dg:cre></dg:cre>'
CALL lineout fsortie, '   <dg:upd></dg:upd>'
CALL lineout fsortie, '</dg:description>'
CALL lineout fsortie, '<channel>'
CALL lineout fsortie, '   <title></title>'
CALL lineout fsortie, '   <description></description>'
CALL lineout fsortie, '   <link></link>'
CALL lineout fsortie, '   <lastBuildDate></lastBuildDate>'
CALL lineout fsortie, ''
RETURN

/* ********************************************************************** */
/*        Termine description                                   */

FDESC:
CALL lineout fsortie, '</description>'
DESCR = 0
RETURN

/* ********************************************************************** */
/*                  Termine un item                                       */

FINITM:
IF SUBSTR(ligne, 1, 7) = 'http://' | SUBSTR(ligne, 1, 7) = 'https:/' then do
    if DESCR = 1 THEN CALL FDESC
    CALL NORMAL
    CALL lineout fsortie, '<link>'ligne'</link>'
    end
ELSE if DESCR=1 then do
    CALL LIG_TRAIT
    CALL FDESC
    end
CALL charout fsortie, '<pubDate></pubDate>'
CALL lineout fsortie, '</item>'
CALL lineout fsortie, ''
CALL INITEM
RETURN
/* ********************************************************************** */
INITEM:
ITEM = 0        /* 1=sortie d'un item commencee   */
TITR = 0        /* 1=2e partie memorisee ; 2=le titre de l'item est sorti */
DESCR = 0       /* 1=sortie description commencee */
ZTITRE = ''
ligne = ''
RETURN
/* ********************************************************************** */
/* Alignement (donnees en UNICODE pour avoir nombre fixe d'octets) */
ALIGNE:
parse arg cod, col, chaine
CALL CONV8_16 cod
cod16 = sortie
lcod = (LENGTH(cod16))
CALL CONV8_16 chaine
chain16 = sortie
posisep = POS(cod16, chain16)
IF posisep > 0 & posisep < 2*col then do
    travf = posisep - 1
    travl = 2*col - travf - lcod
    if posicat > 0 then lcod = 0 /* On conserve la chaine de recherche si categorie FFA */
    tampon = ''
    if travl > 3 then do travl/2
      tampon = tampon || x2c(blanx)
      end
    zone = SUBSTR(chain16, 1, travf) || tampon || SUBSTR(chain16, posisep + lcod) 
    chain16 = zone
    posicat = 0
end

CALL CONV16_8 chain16
ligne = sortie
RETURN

/* ********************************************************************** */
/*           Conversion UTF-8 - UNICODE                                   */
CONV8_16:                                          
parse arg entree                                   
sortie = ''                                        
ZONESORTIE.='NUL'; ZONESORTIE.0=0                  
/*    nom suivi d'un point : c'est un "STEM"  */   
err = systounicode(entree, 'UTF8', , ZONESORTIE.)  
IF err == 0 THEN sortie = ZONESORTIE.!TEXT         
  ELSE sortie = 'probleme car. ' || err            
RETURN                                             
/* ********************************************************************** */
/*           Conversion UTF-16 - UNICODE                                  */
CONV16_8:                                               
parse arg entree                                        
sortie = ''                                             
ZONESORTIE.='NUL'; ZONESORTIE.0=0                       
err = sysfromunicode(entree, 'UTF8', , ' ', ZONESORTIE.)
IF err == 0 THEN sortie = ZONESORTIE.!TEXT              
  ELSE sortie = 'probleme car. ' || err                 
RETURN                                                  
/* ********************************************************************** */
/*                   Normalise des chaines de caractere                   */

NORMAL:
 CALL REMPLACE '&', '&amp;'
 CALL REMPLACE '--', '- - '
 CALL REMPLACE '<', '&lt;'
 CALL REMPLACE "''", '"'
 CALL REMPLACE '09'x, ' ' /* Tabulation */

 CALL REMPLACE '1 000m Marche', '** 1000m'
 CALL REMPLACE '2 000m Marche', '** 2000m'
 CALL REMPLACE '3 000m Marche', '** 3000m'
 CALL REMPLACE '5 000m Marche', '** 5000m'
 CALL REMPLACE '10 000m Marche', '** 10.000m'
 CALL REMPLACE '20 000m Marche', '** 20.000m'
 CALL REMPLACE '30 000m Marche', '** 30.000m'
 CALL REMPLACE '50 000m Marche', '** 50.000m'
 CALL REMPLACE '10 Km Marche', '** 10km'
 CALL REMPLACE '20 Km Marche', '** 20km'
 CALL REMPLACE '35 Km Marche', '** 35km'
 CALL REMPLACE '50 Km Marche', '** 50km'
 CALL REMPLACE '100 Km Marche', '** 100km'
 CALL REMPLACE '10 Minutes Marche', '** 10mn'
 CALL REMPLACE '20 Minutes Marche', '** 20mn'
 CALL REMPLACE '30 Minutes Marche', '** 30mn'
 CALL REMPLACE '45 Minutes Marche', '** 45mn'
 CALL REMPLACE '1 heure Marche', '** 1 heure'
 CALL REMPLACE '1 Heure Marche', '** 1 heure'
 CALL REMPLACE '24 heures Marche', '** 24h'
 CALL REMPLACE '24 Heures Marche', '** 24h'
 CALL REMPLACE 'Grand Fond Marche', '** Grand Fond'

RETURN

/* ********************************************************************** */
              /* Supprime inutile (dans titre) */
NET:
 CALL REMPLACE '\$', ''
 CALL REMPLACE '\#', ''
 CALL REMPLACE '\%', ''
 CALL REMPLACE '09'x, ' ' /* Tabulation */
RETURN

/* ********************************************************************** */
           /* Remplace toute chaine1 par chaine2 dans ligne */
REMPLACE:
parse arg chaine1, chaine2
long1 = LENGTH(chaine1)
apres = ligne
ligne = ''
avant = ''
DO while apres \= ''
   numcar = POS(chaine1,apres)
   if numcar=0 then do
        ligne = ligne || apres
        leave
        end
   avant = avant || substr(apres,1,numcar-1) || chaine2
   ligne = avant
   numcar = numcar+long1
   apres = substr(apres,numcar)
end
RETURN

/* ********************************************************************** */

REGION:
    flag = 0
  CALL LIGF "- I-F" ; if flag \= 0 THEN RETURN
  CALL LIGF "- ARA" ; if flag \= 0 THEN RETURN
  CALL LIGF "- AQU" ; if flag \= 0 THEN RETURN
  CALL LIGF "- BFC" ; if flag \= 0 THEN RETURN
  CALL LIGF "- CEN" ; if flag \= 0 THEN RETURN
  CALL LIGF "- G-E" ; if flag \= 0 THEN RETURN
  CALL LIGF "- H-F" ; if flag \= 0 THEN RETURN
  CALL LIGF "- N-A" ; if flag \= 0 THEN RETURN
  CALL LIGF "- N-C" ; if flag \= 0 THEN RETURN
  CALL LIGF "- NOR" ; if flag \= 0 THEN RETURN
  CALL LIGF "- OCC" ; if flag \= 0 THEN RETURN
  CALL LIGF "- P-L" ; if flag \= 0 THEN RETURN
  CALL LIGF "- PCA" ; if flag \= 0 THEN RETURN
  CALL LIGF "- PYF" ; if flag \= 0 THEN RETURN
  CALL LIGF "- COR" ; if flag \= 0 THEN RETURN
  CALL LIGF "- GUA" ; if flag \= 0 THEN RETURN
  CALL LIGF "- GUY" ; if flag \= 0 THEN RETURN
  CALL LIGF "- MAR" ; if flag \= 0 THEN RETURN
  CALL LIGF "- MAY" ; if flag \= 0 THEN RETURN
  CALL LIGF "- REU" ; if flag \= 0 THEN RETURN
  CALL LIGF "- W-F" ; if flag \= 0 THEN RETURN
  CALL LIGF "- _ET" ; if flag \= 0 THEN RETURN /* Pourrait devenir utile un jour */
  CALL LIGF "- _HF" ; if flag \= 0 THEN RETURN /* Pourrait devenir utile un jour */
/*         Codes ligues obsoletes depuis 2017          */
  CALL LIGF "- ALS" ; if flag \= 0 THEN RETURN
  CALL LIGF "- AUV" ; if flag \= 0 THEN RETURN
  CALL LIGF "- B-N" ; if flag \= 0 THEN RETURN
  CALL LIGF "- BOU" ; if flag \= 0 THEN RETURN
  CALL LIGF "- BRE" ; if flag \= 0 THEN RETURN
  CALL LIGF "- C-A" ; if flag \= 0 THEN RETURN
  CALL LIGF "- CHA" ; if flag \= 0 THEN RETURN
  CALL LIGF "- F-C" ; if flag \= 0 THEN RETURN
  CALL LIGF "- H-N" ; if flag \= 0 THEN RETURN
  CALL LIGF "- LAN" ; if flag \= 0 THEN RETURN
  CALL LIGF "- LIM" ; if flag \= 0 THEN RETURN
  CALL LIGF "- LOR" ; if flag \= 0 THEN RETURN
  CALL LIGF "- NPC" ; if flag \= 0 THEN RETURN
  CALL LIGF "- P-F" ; if flag \= 0 THEN RETURN
  CALL LIGF "- PIC" ; if flag \= 0 THEN RETURN
  CALL LIGF "- POI" ; if flag \= 0 THEN RETURN
  CALL LIGF "- PRO" ; if flag \= 0 THEN RETURN
  CALL LIGF "- PYR" ; if flag \= 0 THEN RETURN
  CALL LIGF "- R-A"
RETURN

/* ********************************************************************** */

LIGF:
parse arg codlig
posdeb = POS(codlig,ligne)
IF posdeb \== 0 then do
    flag = 1
    za = LEFT(ligne,posdeb)
    zb = SUBSTR(ligne,posdeb + 5)
    ligne = '<dg:span class="monosp">' || SUBSTR(codlig,3,3) || '</dg:span> ' || za || zb
end
RETURN

/* ********************************************************************** */

PAYS:
  if SUBSTR(ligne,4,1) == ' ' then do
    p = SUBSTR(ligne,1,3)
IF p='AFG' | p='ALB' | p='ALG' | p='AND' | p='ANG' | p='ANT' | p='ARG' | p='ARM' | p='ARU' | p='ASA' | p='AUS' | ,
   p='AUT' | p='AZE' | p='BAH' | p='BAN' | p='BAR' | p='BDI' | p='BEL' | p='BEN' | p='BER' | p='BGU' | p='BHU' | ,
   p='BIH' | p='BIZ' | p='BLR' | p='BOL' | p='BOT' | p='BRA' | p='BRN' | p='BRU' | p='BUL' | p='BUR' | p='CAF' | ,
   p='CAM' | p='CAN' | p='CAY' | p='CHA' | p='CHI' | p='CHN' | p='CIV' | p='CGO' | p='CMR' | p='COD' | p='COK' | ,
   p='COL' | p='COM' | p='CPV' | p='CRC' | p='CRO' | p='CUB' | p='CYP' | p='CZE' | p='DEN' | p='DJI' | p='DMA' | ,
   p='DOM' | p='ECU' | p='EGY' | p='ERI' | p='ESA' | p='ESP' | p='EST' | p='ETH' | p='FIJ' | p='FIN' | p='FRA' | ,
   p='FSM' | p='GAB' | p='GAM' | p='GBR' | p='GBS' | p='GEO' | p='GEQ' | p='GER' | p='GHA' | p='GRE' | p='GRN' | ,
   p='GUA' | p='GUI' | p='GUM' | p='GUY' | p='HAI' | p='HKG' | p='HON' | p='HUN' | p='INA' | p='IND' | p='IRI' | ,
   p='ISL' | p='ISR' | p='ISV' | p='ITA' | p='IVB' | p='JAM' | p='JOR' | p='JPN' | p='KAZ' | p='KEN' | p='KGZ' | ,
   p='KIR' | p='KOR' | p='KSA' | p='KUW' | p='LAO' | p='LAT' | p='LBA' | p='LBR' | p='LCA' | p='LES' | p='LIB' | ,
   p='LIE' | p='LTU' | p='LUX' | p='MAD' | p='MAR' | p='MAS' | p='MAW' | p='MDA' | p='MDV' | p='MEX' | p='MGL' | ,
   p='MHL' | p='MKD' | p='MLI' | p='MLT' | p='MNE' | p='MON' | p='MOZ' | p='MRI' | p='MTN' | p='MYA' | p='NAM' | ,
   p='NCA' | p='NED' | p='NEP' | p='NGR' | p='NIG' | p='NOR' | p='NRU' | p='NZL' | p='OMA' | p='PAK' | p='PAN' | ,
   p='PAR' | p='PER' | p='PHI' | p='PLE' | p='PLW' | p='PNG' | p='POL' | p='POR' | p='PRK' | p='PUR' | p='QAT' | ,
   p='ROU' | p='RSA' | p='RUS' | p='RWA' | p='SAM' | p='SEN' | p='SEY' | p='SIN' | p='SKN' | p='SLE' | p='SLO' | ,
   p='SMR' | p='SOL' | p='SOM' | p='SRB' | p='SRI' | p='STP' | p='SUD' | p='SUI' | p='SUR' | p='SVK' | p='SWE' | ,
   p='SWZ' | p='SYR' | p='TAN' | p='TGA' | p='THA' | p='TJK' | p='TKM' | p='TLS' | p='TOG' | p='TPE' | p='TTO' | ,
   p='TUN' | p='TUR' | p='TUV' | p='UAE' | p='UGA' | p='UKR' | p='URU' | p='USA' | p='UZB' | p='VAN' | p='VEN' | ,
   p='VIE' | p='VIN' | p='YEM' | p='ZAM' | p='ZIM' then do 
     xlig = '<dg:span class="monosp">' || SUBSTR(ligne,1,3) || '</dg:span>' || SUBSTR(ligne,4)
     ligne = xlig
     end
  end
RETURN

/* ********************************************************************** */

CATEG:
  CALL CATCAT "VEF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "VEM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V1F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V1M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V2F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V2M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V3F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "V3M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "SEF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "SEM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "ESF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "ESM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "JUF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "JUM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "CAF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "CAM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "MIF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "MIM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F35F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M35M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F40F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M40M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F45F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M45M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F50F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M50M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F55F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M55M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F60F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M60M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F65F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M65M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "BEF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "BEM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "POF/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "POM/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F70F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F75F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F80F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F85F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F90F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "F95F/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M70M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M75M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M80M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M85M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M90M/" ; if posicat \= 0 THEN RETURN
  CALL CATCAT "M95M/"
RETURN

/* ********************************************************************** */

CATCAT:
parse arg codcat
posicat = POS(codcat, ligne)
IF posicat > 0 & posicat < 90 then do
    CALL ALIGNE codcat, 90, ligne
    end

RETURN

/* ********************************************************************** */

erreur:
phrase = 'Probleme avec programme ' || src.3 ' ligne ' || SIGL || ' - ' sourceline(SIGL)
say phrase 
IF POS('OS/400', src.1) > 0  then do
  /* IBM AS400 - iSeries */
  'CHGDTAARA DTAARA(*LDA (201 328)) VALUE('''phrase''')'
  EXIT
  end
else do
  /* Windows */
  if POS('WIN', src.1) > 0  then do
     say phrase
     pull
     RETURN phrase
     end
  ELSE
     say phrase
     pull
     RETURN phrase
end
/* ***************************************************************
Ce programme dispose dans un fichier RSS 2.0 le contenu d'un fichier texte    
Cf ~/tekno/manuel/rexxrss2.htm

Groupes de lignes ("items") :
- 1 ou 2 lignes : Titre
- n lignes : Détail 
- dernière ligne (facultatif) Lien http etc.
Pour les resultats fournis par le site FFA athle.org, 
deux lignes de début sont mises en une seule (option "O")
Deplacement du code region ou pays en debut de ligne de titre
Au moins une ligne vide entre chaque item

Alignement automatique
- Categories FFA : posi 90
- \$ mis en 2e colonne : chrono
- \# (position  50) affiliation, naissance…
- \% (position  80) reste…

*/