Subversion Repositories SvarDOS

Rev

Rev 941 | Rev 951 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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