Subversion Repositories SvarDOS

Rev

Rev 946 | Rev 999 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 946 Rev 951
1
<?php /*
1
<?php /*
2
 
2
 
3
  SvarDOS repo index builder
3
  SvarDOS repo index builder
4
  Copyright (C) Mateusz Viste 2012-2022
4
  Copyright (C) Mateusz Viste 2012-2022
5
 
5
 
6
  buildidx computes an index json file for the SvarDOS repository.
6
  buildidx computes an index json file for the SvarDOS repository.
7
  it must be executed pointing to a directory that stores packages (*.svp)
7
  it must be executed pointing to a directory that stores packages (*.svp)
8
  files. buildidx will generate the index file and save it into the package
8
  files. buildidx will generate the index file and save it into the package
9
  repository.
9
  repository.
10
 
10
 
11
  requires php-zip
11
  requires php-zip
12
 
12
 
-
 
13
  24 feb 2022: added hardcoded hack to translate version 'x.xx' to '0.44' (NESticle)
13
  23 feb 2022: basic validation of source archives (not empty + matches an existing svp file)
14
  23 feb 2022: basic validation of source archives (not empty + matches an existing svp file)
14
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
15
  21 feb 2022: buildidx collects categories looking at the dir layout of each package + improved version string parsing (replaced version_compare call by dos_version_compare)
15
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
16
  17 feb 2022: checking for non-8+3 filenames in packages and duplicates + devload no longer part of CORE
16
  16 feb 2022: added warning about overlong version strings and wild files location
17
  16 feb 2022: added warning about overlong version strings and wild files location
17
  15 feb 2022: index is generated as json, contains all filenames and alt versions
18
  15 feb 2022: index is generated as json, contains all filenames and alt versions
18
  14 feb 2022: packages are expected to have the *.svp extension
19
  14 feb 2022: packages are expected to have the *.svp extension
19
  12 feb 2022: skip source packages from being processed (*.src.zip)
20
  12 feb 2022: skip source packages from being processed (*.src.zip)
20
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
21
  20 jan 2022: rewritten the code from ANSI C to PHP for easier maintenance
21
  13 feb 2021: 'title' LSM field is no longer looked after
22
  13 feb 2021: 'title' LSM field is no longer looked after
22
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
23
  11 feb 2021: lsm headers are no longer checked, so it is compatible with the simpler lsm format used by SvarDOS
23
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
24
  13 jan 2021: removed the identification line, changed CRC32 to bsum, not creating the listing.txt file and stopped compressing index
24
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
25
  23 apr 2017: uncompressed index is no longer created, added CRC32 of zib (bin only) files, if present
25
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
26
  28 aug 2016: listing.txt is always written inside the repo dir (instead of inside current dir)
26
  27 aug 2016: accepting full paths to repos (starting with /...)
27
  27 aug 2016: accepting full paths to repos (starting with /...)
27
  07 dec 2013: rewritten buildidx in ANSI C89
28
  07 dec 2013: rewritten buildidx in ANSI C89
28
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
29
  19 aug 2013: add a compressed version of the index file to repos (index.gz)
29
  22 jul 2013: creating a listing.txt file with list of packages
30
  22 jul 2013: creating a listing.txt file with list of packages
30
  18 jul 2013: writing the number of packaged into the first line of the lst file
31
  18 jul 2013: writing the number of packaged into the first line of the lst file
31
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
32
  11 jul 2013: added a switch to 7za to make it case insensitive when extracting lsm files
32
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
33
  10 jul 2013: changed unzip calls to 7za (to handle cases when appinfo is compressed with lzma)
33
  04 feb 2013: added CRC32 support
34
  04 feb 2013: added CRC32 support
34
  22 sep 2012: forked 1st version from FDUPDATE builder
35
  22 sep 2012: forked 1st version from FDUPDATE builder
35
*/
36
*/
36
 
37
 
37
$PVER = "20220223";
38
$PVER = "20220224";
38
 
39
 
39
 
40
 
40
// computes the BSD sum of a file and returns it
41
// computes the BSD sum of a file and returns it
41
function file2bsum($fname) {
42
function file2bsum($fname) {
42
  $result = 0;
43
  $result = 0;
43
 
44
 
44
  $fd = fopen($fname, 'rb');
45
  $fd = fopen($fname, 'rb');
45
  if ($fd === false) return(0);
46
  if ($fd === false) return(0);
46
 
47
 
47
  while (!feof($fd)) {
48
  while (!feof($fd)) {
48
 
49
 
49
    $buff = fread($fd, 1024 * 1024);
50
    $buff = fread($fd, 1024 * 1024);
50
 
51
 
51
    $slen = strlen($buff);
52
    $slen = strlen($buff);
52
    for ($i = 0; $i < $slen; $i++) {
53
    for ($i = 0; $i < $slen; $i++) {
53
      // rotr
54
      // rotr
54
      $result = ($result >> 1) | ($result << 15);
55
      $result = ($result >> 1) | ($result << 15);
55
      // add and truncate to 16 bits
56
      // add and truncate to 16 bits
56
      $result += ord($buff[$i]);
57
      $result += ord($buff[$i]);
57
      $result &= 0xffff;
58
      $result &= 0xffff;
58
    }
59
    }
59
  }
60
  }
60
 
61
 
61
  fclose($fd);
62
  fclose($fd);
62
  return($result);
63
  return($result);
63
}
64
}
64
 
65
 
65
 
66
 
66
// translates a version string into a array of integer values.
67
// translates a version string into a array of integer values.
67
// Accepted formats follow:
68
// Accepted formats follow:
68
//    300.12.1
69
//    300.12.1
69
//    1
70
//    1
70
//    12.2.34.2-4.5
71
//    12.2.34.2-4.5
71
//    1.2c
72
//    1.2c
72
//    1.01 beta+3
73
//    1.01 beta+3
73
//    2013-12-31
74
//    2013-12-31
74
//    20220222 alpha
75
//    20220222 alpha
75
function vertoarr($verstr) {
76
function vertoarr($verstr) {
76
  $subver = array(0,0,0,0);
77
  $subver = array(0,0,0,0);
77
 
78
 
78
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
79
  // switch string to lcase for easier processing and trim any leading or trailing white spaces
79
  $verstr = strtolower(trim($verstr));
80
  $verstr = strtolower(trim($verstr));
80
 
81
 
81
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
82
  // replace all '-' and '/' characters to '.' (uniformization of sub-version parts delimiters)
82
  $verstr = strtr($verstr, '-/', '..');
83
  $verstr = strtr($verstr, '-/', '..');
83
 
84
 
84
  // is there a subversion value? (for example "+4" in "1.05+4")
85
  // is there a subversion value? (for example "+4" in "1.05+4")
85
  $i = strrpos($verstr, '+', 1);
86
  $i = strrpos($verstr, '+', 1);
86
  if ($i !== false) {
87
  if ($i !== false) {
87
    // validate the svar-version is a proper integer
88
    // validate the svar-version is a proper integer
88
    $svarver = substr($verstr, $i + 1);
89
    $svarver = substr($verstr, $i + 1);
89
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
90
    if (! preg_match('/[1-9][0-9]*/', $svarver)) {
90
      return(false);
91
      return(false);
91
    }
92
    }
92
    $subver[3] = intval($svarver); // set the +rev as a very minor item
93
    $subver[3] = intval($svarver); // set the +rev as a very minor item
93
    $verstr = substr($verstr, 0, $i);
94
    $verstr = substr($verstr, 0, $i);
94
  }
95
  }
95
 
96
 
-
 
97
  // NESticls hack: version "x.xx" is translated to "0.44"... that sucks but that's how it is.
-
 
98
  // ref: https://web.archive.org/web/20070205074631/http://www.zophar.net/NESticle/
-
 
99
  if ($verstr == 'x.xx') $verstr = '0.44';
-
 
100
 
96
  // beta reordering: convert "beta 0.95" to "0.95 beta"
101
  // beta reordering: convert "beta 0.95" to "0.95 beta"
97
  if (preg_match('/^beta /', $verstr)) $verstr = substr($verstr, 5) . ' beta';
102
  if (preg_match('/^beta /', $verstr)) $verstr = substr($verstr, 5) . ' beta';
98
 
103
 
99
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
104
  // any occurence of alpha,beta,gamma,delta etc preceded by a digit should have a space separator added
100
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
105
  // example: "2.6.0pre9" becomes "2.6.0 pre9"
101
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
106
  $verstr = preg_replace('/([0-9])(alpha|beta|gamma|delta|pre|rc|patch)/', '$1 $2', $verstr);
102
 
107
 
103
  // same as above, but this time adding a trailing space separator
108
  // same as above, but this time adding a trailing space separator
104
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
109
  // example: "2.6.0 pre9" becomes "2.6.0 pre 9"
105
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
110
  $verstr = preg_replace('/(alpha|beta|gamma|delta|pre|rc|patch)([0-9])/', '$1 $2', $verstr);
106
 
111
 
107
  // is the version ending with ' alpha', 'beta', etc?
112
  // is the version ending with ' alpha', 'beta', etc?
108
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
113
  if (preg_match('/ (alpha|beta|gamma|delta|pre|rc|patch)( [0-9]{1,4}){0,1}$/', $verstr)) {
109
    // if there is a trailing beta-number, process it first
114
    // if there is a trailing beta-number, process it first
110
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
115
    if (preg_match('/ [0-9]{1,4}$/', $verstr)) {
111
      $i = strrpos($verstr, ' ');
116
      $i = strrpos($verstr, ' ');
112
      $subver[2] = intval(substr($verstr, $i + 1));
117
      $subver[2] = intval(substr($verstr, $i + 1));
113
      $verstr = trim(substr($verstr, 0, $i));
118
      $verstr = trim(substr($verstr, 0, $i));
114
    }
119
    }
115
    $i = strrpos($verstr, ' ');
120
    $i = strrpos($verstr, ' ');
116
    $greek = substr($verstr, $i + 1);
121
    $greek = substr($verstr, $i + 1);
117
    $verstr = trim(substr($verstr, 0, $i));
122
    $verstr = trim(substr($verstr, 0, $i));
118
    if ($greek == 'alpha') {
123
    if ($greek == 'alpha') {
119
      $subver[1] = 1;
124
      $subver[1] = 1;
120
    } else if ($greek == 'beta') {
125
    } else if ($greek == 'beta') {
121
      $subver[1] = 2;
126
      $subver[1] = 2;
122
    } else if ($greek == 'gamma') {
127
    } else if ($greek == 'gamma') {
123
      $subver[1] = 3;
128
      $subver[1] = 3;
124
    } else if ($greek == 'delta') {
129
    } else if ($greek == 'delta') {
125
      $subver[1] = 4;
130
      $subver[1] = 4;
126
    } else if ($greek == 'pre') {
131
    } else if ($greek == 'pre') {
127
      $subver[1] = 5;
132
      $subver[1] = 5;
128
    } else if ($greek == 'rc') {
133
    } else if ($greek == 'rc') {
129
      $subver[1] = 6;
134
      $subver[1] = 6;
130
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
135
    } else if ($greek == 'patch') { // this is a POST-release version, as opposed to all above that are PRE-release versions
131
      $subver[1] = 99;
136
      $subver[1] = 99;
132
    } else {
137
    } else {
133
      return(false);
138
      return(false);
134
    }
139
    }
135
  } else {
140
  } else {
136
    $subver[1] = 98; // one less than the 'patch' level
141
    $subver[1] = 98; // one less than the 'patch' level
137
  }
142
  }
138
 
143
 
139
  // does the version string have a single-letter subversion? (1.0c)
144
  // does the version string have a single-letter subversion? (1.0c)
140
  if (preg_match('/[a-z]$/', $verstr)) {
145
  if (preg_match('/[a-z]$/', $verstr)) {
141
    $subver[0] = ord(substr($verstr, -1));
146
    $subver[0] = ord(substr($verstr, -1));
142
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
147
    $verstr = substr_replace($verstr, '', -1); // remove last character from string
143
  }
148
  }
144
 
149
 
145
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
150
  // convert "30-jan-99", "1999-jan-30" and "30-jan-1999" versions to "30jan99" or "30jan1999"
146
  // note that dashes have already been replaced by dots
151
  // note that dashes have already been replaced by dots
147
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
152
  if (preg_match('/^([0-9][0-9]){1,2}\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.([0-9][0-9]){1,2}$/', $verstr)) {
148
    $verstr = str_replace('.', '', $verstr);
153
    $verstr = str_replace('.', '', $verstr);
149
  }
154
  }
150
 
155
 
151
  // convert "2009mar17" versions to "17mar2009"
156
  // convert "2009mar17" versions to "17mar2009"
152
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
157
  if (preg_match('/^[0-9]{4}(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/', $verstr)) {
153
    $dy = substr($verstr, 7);
158
    $dy = substr($verstr, 7);
154
    $mo = substr($verstr, 4, 3);
159
    $mo = substr($verstr, 4, 3);
155
    $ye = substr($verstr, 0, 4);
160
    $ye = substr($verstr, 0, 4);
156
    $verstr = "{$dy}{$mo}{$ye}";
161
    $verstr = "{$dy}{$mo}{$ye}";
157
  }
162
  }
158
 
163
 
159
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
164
  // convert "30jan99" versions to 99.1.30 and "30jan1999" to 1999.1.30
160
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
165
  if (preg_match('/^[0-3][0-9](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)([0-9][0-9]){1,2}$/', $verstr)) {
161
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
166
    $months = array('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12);
162
    $dy = substr($verstr, 0, 2);
167
    $dy = substr($verstr, 0, 2);
163
    $mo = $months[substr($verstr, 2, 3)];
168
    $mo = $months[substr($verstr, 2, 3)];
164
    $ye = substr($verstr, 5);
169
    $ye = substr($verstr, 5);
165
    $verstr = "{$ye}.{$mo}.{$dy}";
170
    $verstr = "{$ye}.{$mo}.{$dy}";
166
  }
171
  }
167
 
172
 
168
  // validate the format is supported, should be something no more complex than 1.05.3.33
173
  // validate the format is supported, should be something no more complex than 1.05.3.33
169
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
174
  if (! preg_match('/^[0-9][0-9.]{0,20}$/', $verstr)) {
170
    return(false);
175
    return(false);
171
  }
176
  }
172
 
177
 
173
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
178
  // NOTE: a zero right after a separator and trailed with a digit (as in 1.01)
174
  //       has a special meaning
179
  //       has a special meaning
175
  $exploded = explode('.', $verstr);
180
  $exploded = explode('.', $verstr);
176
  if (count($exploded) > 16) {
181
  if (count($exploded) > 16) {
177
    return(false);
182
    return(false);
178
  }
183
  }
179
  $exploded[16] = $subver[0]; // a-z (1.0c)
184
  $exploded[16] = $subver[0]; // a-z (1.0c)
180
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
185
  $exploded[17] = $subver[1]; // alpha/beta/gamma/delta/rc/pre
181
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
186
  $exploded[18] = $subver[2]; // alpha-beta-gamma subversion (eg. "beta 9")
182
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
187
  $exploded[19] = $subver[3]; // svar-ver (1.0+5)
183
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
188
  for ($i = 0; $i < 20; $i++) if (empty($exploded[$i])) $exploded[$i] = '0';
184
 
189
 
185
  ksort($exploded);
190
  ksort($exploded);
186
 
191
 
187
  return($exploded);
192
  return($exploded);
188
}
193
}
189
 
194
 
190
 
195
 
191
function dos_version_compare($v1, $v2) {
196
function dos_version_compare($v1, $v2) {
192
  $v1arr = vertoarr($v1);
197
  $v1arr = vertoarr($v1);
193
  $v2arr = vertoarr($v2);
198
  $v2arr = vertoarr($v2);
194
  for ($i = 0; $i < count($v1arr); $i++) {
199
  for ($i = 0; $i < count($v1arr); $i++) {
195
    if ($v1arr[$i] > $v2arr[$i]) return(1);
200
    if ($v1arr[$i] > $v2arr[$i]) return(1);
196
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
201
    if ($v1arr[$i] < $v2arr[$i]) return(-1);
197
  }
202
  }
198
  return(0);
203
  return(0);
199
}
204
}
200
 
205
 
201
 
206
 
202
// reads file fil from zip archive z and returns its content, or false on error
207
// reads file fil from zip archive z and returns its content, or false on error
203
function read_file_from_zip($z, $fil) {
208
function read_file_from_zip($z, $fil) {
204
  $zip = new ZipArchive;
209
  $zip = new ZipArchive;
205
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
210
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
206
    echo "ERROR: failed to open zip file '{$z}'\n";
211
    echo "ERROR: failed to open zip file '{$z}'\n";
207
    return(false);
212
    return(false);
208
  }
213
  }
209
 
214
 
210
  // load the appinfo/pkgname.lsm file
215
  // load the appinfo/pkgname.lsm file
211
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
216
  $res = $zip->getFromName($fil, 8192, ZipArchive::FL_NOCASE);
212
 
217
 
213
  $zip->close();
218
  $zip->close();
214
  return($res);
219
  return($res);
215
}
220
}
216
 
221
 
217
 
222
 
218
function read_list_of_files_in_zip($z) {
223
function read_list_of_files_in_zip($z) {
219
  $zip = new ZipArchive;
224
  $zip = new ZipArchive;
220
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
225
  if ($zip->open($z, ZipArchive::RDONLY) !== true) {
221
    echo "ERROR: failed to open zip file '{$z}'\n";
226
    echo "ERROR: failed to open zip file '{$z}'\n";
222
    return(false);
227
    return(false);
223
  }
228
  }
224
 
229
 
225
  $res = array();
230
  $res = array();
226
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
231
  for ($i = 0; $i < $zip->numFiles; $i++) $res[] = $zip->getNameIndex($i);
227
 
232
 
228
  $zip->close();
233
  $zip->close();
229
  return($res);
234
  return($res);
230
}
235
}
231
 
236
 
232
 
237
 
233
// reads a LSM string and returns it in the form of an array
238
// reads a LSM string and returns it in the form of an array
234
function parse_lsm($s) {
239
function parse_lsm($s) {
235
  $res = array();
240
  $res = array();
236
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
241
  for ($l = strtok($s, "\n"); $l !== false; $l = strtok("\n")) {
237
    // the line is "token: value", let's find the colon
242
    // the line is "token: value", let's find the colon
238
    $colpos = strpos($l, ':');
243
    $colpos = strpos($l, ':');
239
    if (($colpos === false) || ($colpos === 0)) continue;
244
    if (($colpos === false) || ($colpos === 0)) continue;
240
    $tok = strtolower(trim(substr($l, 0, $colpos)));
245
    $tok = strtolower(trim(substr($l, 0, $colpos)));
241
    $val = trim(substr($l, $colpos + 1));
246
    $val = trim(substr($l, $colpos + 1));
242
    $res[$tok] = $val;
247
    $res[$tok] = $val;
243
  }
248
  }
244
  return($res);
249
  return($res);
245
}
250
}
246
 
251
 
247
 
252
 
248
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
253
// on PHP 8+ there is str_starts_with(), but not on PHP 7 so I use this
249
function str_head_is($haystack, $needle) {
254
function str_head_is($haystack, $needle) {
250
  return strpos($haystack, $needle) === 0;
255
  return strpos($haystack, $needle) === 0;
251
}
256
}
252
 
257
 
253
 
258
 
254
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
259
// returns an array that contains CORE packages (populated from the core subdirectory in pkgdir)
255
function load_core_list($repodir) {
260
function load_core_list($repodir) {
256
  $res = array();
261
  $res = array();
257
 
262
 
258
  foreach (scandir($repodir . '/core/') as $f) {
263
  foreach (scandir($repodir . '/core/') as $f) {
259
    if (!preg_match('/\.svp$/', $f)) continue;
264
    if (!preg_match('/\.svp$/', $f)) continue;
260
    $res[] = explode('.', $f)[0];
265
    $res[] = explode('.', $f)[0];
261
  }
266
  }
262
  return($res);
267
  return($res);
263
}
268
}
264
 
269
 
265
 
270
 
266
// ***************** MAIN ROUTINE *********************************************
271
// ***************** MAIN ROUTINE *********************************************
267
 
272
 
268
//echo "SvarDOS repository index generator ver {$PVER}\n";
273
//echo "SvarDOS repository index generator ver {$PVER}\n";
269
 
274
 
270
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
275
if (($_SERVER['argc'] != 2) || ($_SERVER['argv'][1][0] == '-')) {
271
  echo "usage: php buildidx.php repodir\n";
276
  echo "usage: php buildidx.php repodir\n";
272
  exit(1);
277
  exit(1);
273
}
278
}
274
 
279
 
275
$repodir = $_SERVER['argv'][1];
280
$repodir = $_SERVER['argv'][1];
276
 
281
 
277
$pkgfiles = scandir($repodir);
282
$pkgfiles = scandir($repodir);
278
$pkgcount = 0;
283
$pkgcount = 0;
279
 
284
 
280
 
285
 
281
// load the list of CORE and MSDOS_COMPAT packages
286
// load the list of CORE and MSDOS_COMPAT packages
282
 
287
 
283
$core_packages_list = load_core_list($repodir);
288
$core_packages_list = load_core_list($repodir);
284
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
289
$msdos_compat_list = explode(' ', 'append assign attrib chkdsk choice command comp cpidos debug defrag deltree diskcomp diskcopy display edit edlin exe2bin fc fdapm fdisk find format help himemx kernel keyb label localcfg mem mirror mode more move nlsfunc print replace share shsucdx sort swsubst tree undelete unformat xcopy');
285
 
290
 
286
// do a list of all svp packages with their available versions and descriptions
291
// do a list of all svp packages with their available versions and descriptions
287
 
292
 
288
$pkgdb = array();
293
$pkgdb = array();
289
foreach ($pkgfiles as $fname) {
294
foreach ($pkgfiles as $fname) {
290
 
295
 
291
  // zip files (ie. source archives)
296
  // zip files (ie. source archives)
292
  if (preg_match('/\.zip$/', $fname)) {
297
  if (preg_match('/\.zip$/', $fname)) {
293
    // the zip archive should contain at least one file
298
    // the zip archive should contain at least one file
294
    if (count(read_list_of_files_in_zip($repodir . '/' . $fname)) < 1) echo "WARNING: source archive {$fname} contains no files (either empty or corrupted)\n";
299
    if (count(read_list_of_files_in_zip($repodir . '/' . $fname)) < 1) echo "WARNING: source archive {$fname} contains no files (either empty or corrupted)\n";
295
    // check that the file relates to an existing svp package
300
    // check that the file relates to an existing svp package
296
    $svpfname = preg_replace('/zip$/', 'svp', $fname);
301
    $svpfname = preg_replace('/zip$/', 'svp', $fname);
297
    if (!file_exists($repodir . '/' . $svpfname)) echo "ERROR: orphaned source archive '{$fname}' (no matching svp file, expecting a package named '{$svpfname}')\n";
302
    if (!file_exists($repodir . '/' . $svpfname)) echo "ERROR: orphaned source archive '{$fname}' (no matching svp file, expecting a package named '{$svpfname}')\n";
298
    // that is for zip files
303
    // that is for zip files
299
    continue;
304
    continue;
300
  }
305
  }
301
 
306
 
302
  // skip (and warn about) non-svp
307
  // skip (and warn about) non-svp
303
  if (!preg_match('/\.svp$/', $fname)) {
308
  if (!preg_match('/\.svp$/', $fname)) {
304
    $okfiles = array('.', '..', '_cats.json', '_index.json', '_buildidx.log', 'core');
309
    $okfiles = array('.', '..', '_cats.json', '_index.json', '_buildidx.log', 'core');
305
    if (array_search($fname, $okfiles) !== false) continue;
310
    if (array_search($fname, $okfiles) !== false) continue;
306
    echo "WARNING: wild file '{$fname} (this is either an useless file that should be removed, or a misnamed package or source archive)'\n";
311
    echo "WARNING: wild file '{$fname} (this is either an useless file that should be removed, or a misnamed package or source archive)'\n";
307
    continue;
312
    continue;
308
  }
313
  }
309
 
314
 
310
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
315
  if (!preg_match('/^[a-zA-Z0-9+. _-]*\.svp$/', $fname)) {
311
    echo "ERROR: {$fname} has a very weird name\n";
316
    echo "ERROR: {$fname} has a very weird name\n";
312
    continue;
317
    continue;
313
  }
318
  }
314
 
319
 
315
  $path_parts = pathinfo($fname);
320
  $path_parts = pathinfo($fname);
316
  $pkgnam = explode('-', $path_parts['filename'])[0];
321
  $pkgnam = explode('-', $path_parts['filename'])[0];
317
  $pkgfullpath = realpath($repodir . '/' . $fname);
322
  $pkgfullpath = realpath($repodir . '/' . $fname);
318
 
323
 
319
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
324
  $lsm = read_file_from_zip($pkgfullpath, "appinfo/{$pkgnam}.lsm");
320
  if ($lsm == false) {
325
  if ($lsm == false) {
321
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
326
    echo "ERROR: {$fname} does not contain an LSM file at the expected location\n";
322
    continue;
327
    continue;
323
  }
328
  }
324
  $lsmarray = parse_lsm($lsm);
329
  $lsmarray = parse_lsm($lsm);
325
  if (empty($lsmarray['version'])) {
330
  if (empty($lsmarray['version'])) {
326
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
331
    echo "ERROR: lsm file in {$fname} does not contain a version\n";
327
    continue;
332
    continue;
328
  }
333
  }
329
  if (strlen($lsmarray['version']) > 16) {
334
  if (strlen($lsmarray['version']) > 16) {
330
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
335
    echo "ERROR: version string in lsm file of {$fname} is too long (16 chars max)\n";
331
    continue;
336
    continue;
332
  }
337
  }
333
  if (empty($lsmarray['description'])) {
338
  if (empty($lsmarray['description'])) {
334
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
339
    echo "ERROR: lsm file in {$fname} does not contain a description\n";
335
    continue;
340
    continue;
336
  }
341
  }
337
 
342
 
338
  // validate the files present in the archive
343
  // validate the files present in the archive
339
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
344
  $listoffiles = read_list_of_files_in_zip($pkgfullpath);
340
  $pkgdir = $pkgnam;
345
  $pkgdir = $pkgnam;
341
 
346
 
342
  // special rule for "parent and children" packages
347
  // special rule for "parent and children" packages
343
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
348
  if (str_head_is($pkgnam, 'djgpp_')) $pkgdir = 'djgpp'; // djgpp_* packages put their files in djgpp
344
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
349
  if ($pkgnam == 'fbc_help') $pkgdir = 'fbc'; // FreeBASIC help goes to the FreeBASIC dir
345
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
350
  if ($pkgnam == 'clamdb') $pkgdir = 'clamav'; // data patterns for clamav
346
 
351
 
347
  // array used to detect duplicated entries after lower-case conversion
352
  // array used to detect duplicated entries after lower-case conversion
348
  $duparr = array();
353
  $duparr = array();
349
 
354
 
350
  // will hold the list of categories that this package belongs to
355
  // will hold the list of categories that this package belongs to
351
  $catlist = array();
356
  $catlist = array();
352
 
357
 
353
  foreach ($listoffiles as $f) {
358
  foreach ($listoffiles as $f) {
354
    $f = strtolower($f);
359
    $f = strtolower($f);
355
    $path_array = explode('/', $f);
360
    $path_array = explode('/', $f);
356
    // emit a warning when non-8+3 filenames are spotted and find duplicates
361
    // emit a warning when non-8+3 filenames are spotted and find duplicates
357
    foreach ($path_array as $item) {
362
    foreach ($path_array as $item) {
358
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
363
      if (empty($item)) continue; // skip empty items at end of paths (eg. appinfo/)
359
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
364
      if (!preg_match("/[a-z0-9!#$%&'()@^_`{}~-]{1,8}(\.[a-z0-9!#$%&'()@^_`{}~-]{1,3}){0,1}/", $item)) {
360
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
365
        echo "WARNING: {$fname} contains a non-8+3 path (or weird char): {$item} (in $f)\n";
361
      }
366
      }
362
    }
367
    }
363
    // look for dups
368
    // look for dups
364
    if (array_search($f, $duparr) !== false) {
369
    if (array_search($f, $duparr) !== false) {
365
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
370
      echo "WARNING: {$fname} contains a duplicated entry: '{$f}'\n";
366
    } else {
371
    } else {
367
      $duparr[] = $f;
372
      $duparr[] = $f;
368
    }
373
    }
369
    // LSM file is ok
374
    // LSM file is ok
370
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
375
    if ($f === "appinfo/{$pkgnam}.lsm") continue;
371
    if ($f === "appinfo/") continue;
376
    if ($f === "appinfo/") continue;
372
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
377
    // CORE and MSDOS_COMPAT packages are premium citizens and can do a little more
373
    $core_or_msdoscompat = 0;
378
    $core_or_msdoscompat = 0;
374
    if (array_search($pkgnam, $core_packages_list) !== false) {
379
    if (array_search($pkgnam, $core_packages_list) !== false) {
375
      $catlist[] = 'core';
380
      $catlist[] = 'core';
376
      $core_or_msdoscompat = 1;
381
      $core_or_msdoscompat = 1;
377
    }
382
    }
378
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
383
    if (array_search($pkgnam, $msdos_compat_list) !== false) {
379
      $catlist[] = 'msdos_compat';
384
      $catlist[] = 'msdos_compat';
380
      $core_or_msdoscompat = 1;
385
      $core_or_msdoscompat = 1;
381
    }
386
    }
382
    if ($core_or_msdoscompat == 1) {
387
    if ($core_or_msdoscompat == 1) {
383
      if (str_head_is($f, 'bin/')) continue;
388
      if (str_head_is($f, 'bin/')) continue;
384
      if (str_head_is($f, 'cpi/')) continue;
389
      if (str_head_is($f, 'cpi/')) continue;
385
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
390
      if (str_head_is($f, "doc/{$pkgdir}/")) continue;
386
      if ($f === 'doc/') continue;
391
      if ($f === 'doc/') continue;
387
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
392
      if (str_head_is($f, "nls/{$pkgdir}.")) continue;
388
      if ($f === 'nls/') continue;
393
      if ($f === 'nls/') continue;
389
    }
394
    }
390
    // the help package is allowed to put files in... help
395
    // the help package is allowed to put files in... help
391
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
396
    if (($pkgnam == 'help') && (str_head_is($f, 'help/'))) continue;
392
    // must be category-prefixed file, add it to the list of categories for this package
397
    // must be category-prefixed file, add it to the list of categories for this package
393
    $catlist[] = explode('/', $f)[0];
398
    $catlist[] = explode('/', $f)[0];
394
    // well-known "category" dirs are okay
399
    // well-known "category" dirs are okay
395
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
400
    if (str_head_is($f, "progs/{$pkgdir}/")) continue;
396
    if ($f === 'progs/') continue;
401
    if ($f === 'progs/') continue;
397
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
402
    if (str_head_is($f, "devel/{$pkgdir}/")) continue;
398
    if ($f === 'devel/') continue;
403
    if ($f === 'devel/') continue;
399
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
404
    if (str_head_is($f, "games/{$pkgdir}/")) continue;
400
    if ($f === 'games/') continue;
405
    if ($f === 'games/') continue;
401
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
406
    if (str_head_is($f, "drivers/{$pkgdir}/")) continue;
402
    if ($f === 'drivers/') continue;
407
    if ($f === 'drivers/') continue;
403
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
408
    echo "WARNING: {$fname} contains a file in an illegal location: {$f}\n";
404
  }
409
  }
405
 
410
 
406
  // do I understand the version string?
411
  // do I understand the version string?
407
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
412
  if (vertoarr($lsmarray['version']) === false) echo "WARNING: {$fname} parsing of version string failed ('{$lsmarray['version']}')\n";
408
 
413
 
409
  $meta['fname'] = $fname;
414
  $meta['fname'] = $fname;
410
  $meta['desc'] = $lsmarray['description'];
415
  $meta['desc'] = $lsmarray['description'];
411
  $meta['cats'] = array_unique($catlist);
416
  $meta['cats'] = array_unique($catlist);
412
 
417
 
413
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
418
  $pkgdb[$pkgnam][$lsmarray['version']] = $meta;
414
}
419
}
415
 
420
 
416
 
421
 
417
$db = array();
422
$db = array();
418
$cats = array();
423
$cats = array();
419
 
424
 
420
// ******** compute the version-sorted list of packages with a single *********
425
// ******** compute the version-sorted list of packages with a single *********
421
// ******** description and category list for each package ********************
426
// ******** description and category list for each package ********************
422
 
427
 
423
// iterate over each svp package
428
// iterate over each svp package
424
foreach ($pkgdb as $pkg => $versions) {
429
foreach ($pkgdb as $pkg => $versions) {
425
 
430
 
426
  // sort filenames by version, highest first
431
  // sort filenames by version, highest first
427
  uksort($versions, "dos_version_compare");
432
  uksort($versions, "dos_version_compare");
428
  $versions = array_reverse($versions, true);
433
  $versions = array_reverse($versions, true);
429
 
434
 
430
  foreach ($versions as $ver => $meta) {
435
  foreach ($versions as $ver => $meta) {
431
    $fname = $meta['fname'];
436
    $fname = $meta['fname'];
432
    $desc = $meta['desc'];
437
    $desc = $meta['desc'];
433
 
438
 
434
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
439
    $bsum = file2bsum(realpath($repodir . '/' . $fname));
435
 
440
 
436
    $meta2['ver'] = strval($ver);
441
    $meta2['ver'] = strval($ver);
437
    $meta2['bsum'] = $bsum;
442
    $meta2['bsum'] = $bsum;
438
 
443
 
439
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
444
    if (empty($db[$pkg]['desc'])) $db[$pkg]['desc'] = $desc;
440
    if (empty($db[$pkg]['cats'])) {
445
    if (empty($db[$pkg]['cats'])) {
441
      $db[$pkg]['cats'] = $meta['cats'];
446
      $db[$pkg]['cats'] = $meta['cats'];
442
      $cats = array_unique(array_merge($cats, $meta['cats']));
447
      $cats = array_unique(array_merge($cats, $meta['cats']));
443
    }
448
    }
444
    $db[$pkg]['versions'][$fname] = $meta2;
449
    $db[$pkg]['versions'][$fname] = $meta2;
445
  }
450
  }
446
 
451
 
447
  $pkgcount++;
452
  $pkgcount++;
448
 
453
 
449
}
454
}
450
 
455
 
451
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
456
if ($pkgcount < 100) echo "WARNING: an unexpectedly low number of packages has been found in the repo ({$pkgcount})\n";
452
 
457
 
453
$json_blob = json_encode($db);
458
$json_blob = json_encode($db);
454
if ($json_blob === false) {
459
if ($json_blob === false) {
455
  echo "ERROR: JSON convertion failed! -> ";
460
  echo "ERROR: JSON convertion failed! -> ";
456
  switch (json_last_error()) {
461
  switch (json_last_error()) {
457
    case JSON_ERROR_DEPTH:
462
    case JSON_ERROR_DEPTH:
458
      echo 'maximum stack depth exceeded';
463
      echo 'maximum stack depth exceeded';
459
      break;
464
      break;
460
    case JSON_ERROR_STATE_MISMATCH:
465
    case JSON_ERROR_STATE_MISMATCH:
461
      echo 'underflow of the modes mismatch';
466
      echo 'underflow of the modes mismatch';
462
      break;
467
      break;
463
    case JSON_ERROR_CTRL_CHAR:
468
    case JSON_ERROR_CTRL_CHAR:
464
      echo 'unexpected control character found';
469
      echo 'unexpected control character found';
465
      break;
470
      break;
466
    case JSON_ERROR_UTF8:
471
    case JSON_ERROR_UTF8:
467
      echo 'malformed utf-8 characters';
472
      echo 'malformed utf-8 characters';
468
      break;
473
      break;
469
    default:
474
    default:
470
      echo "unknown error";
475
      echo "unknown error";
471
      break;
476
      break;
472
  }
477
  }
473
  echo "\n";
478
  echo "\n";
474
}
479
}
475
 
480
 
476
file_put_contents($repodir . '/_index.json', $json_blob);
481
file_put_contents($repodir . '/_index.json', $json_blob);
477
 
482
 
478
$cats_json = json_encode($cats);
483
$cats_json = json_encode($cats);
479
file_put_contents($repodir . '/_cats.json', $cats_json);
484
file_put_contents($repodir . '/_cats.json', $cats_json);
480
 
485
 
481
exit(0);
486
exit(0);
482
 
487
 
483
?>
488
?>
484
 
489